Show More
@@ -0,0 +1,92 b'' | |||||
|
1 | import sys, textwrap | |||
|
2 | # import from the live mercurial repo | |||
|
3 | sys.path.insert(0, "..") | |||
|
4 | from mercurial.commands import table, globalopts | |||
|
5 | from mercurial.i18n import gettext as _ | |||
|
6 | ||||
|
7 | def get_desc(docstr): | |||
|
8 | if not docstr: | |||
|
9 | return "", "" | |||
|
10 | # sanitize | |||
|
11 | docstr = docstr.strip("\n") | |||
|
12 | docstr = docstr.rstrip() | |||
|
13 | shortdesc = docstr.splitlines()[0].strip() | |||
|
14 | ||||
|
15 | i = docstr.find("\n") | |||
|
16 | if i != -1: | |||
|
17 | desc = docstr[i+2:] | |||
|
18 | else: | |||
|
19 | desc = " %s" % shortdesc | |||
|
20 | return (shortdesc, desc) | |||
|
21 | ||||
|
22 | def get_opts(opts): | |||
|
23 | for shortopt, longopt, default, desc in opts: | |||
|
24 | allopts = [] | |||
|
25 | if shortopt: | |||
|
26 | allopts.append("-%s" % shortopt) | |||
|
27 | if longopt: | |||
|
28 | allopts.append("--%s" % longopt) | |||
|
29 | desc += default and _(" (default: %s)") % default or "" | |||
|
30 | yield(", ".join(allopts), desc) | |||
|
31 | ||||
|
32 | def get_cmd(cmd): | |||
|
33 | d = {} | |||
|
34 | attr = table[cmd] | |||
|
35 | cmds = cmd.lstrip("^").split("|") | |||
|
36 | ||||
|
37 | d['synopsis'] = attr[2] | |||
|
38 | d['cmd'] = cmds[0] | |||
|
39 | d['aliases'] = cmd.split("|")[1:] | |||
|
40 | d['desc'] = get_desc(attr[0].__doc__) | |||
|
41 | d['opts'] = list(get_opts(attr[1])) | |||
|
42 | return d | |||
|
43 | ||||
|
44 | ||||
|
45 | def show_doc(ui): | |||
|
46 | def bold(s, text=""): | |||
|
47 | ui.write("%s\n%s\n%s\n" % (s, "="*len(s), text)) | |||
|
48 | def underlined(s, text=""): | |||
|
49 | ui.write("%s\n%s\n%s\n" % (s, "-"*len(s), text)) | |||
|
50 | ||||
|
51 | # print options | |||
|
52 | underlined(_("OPTIONS")) | |||
|
53 | for optstr, desc in get_opts(globalopts): | |||
|
54 | ui.write("%s::\n %s\n\n" % (optstr, desc)) | |||
|
55 | ||||
|
56 | # print cmds | |||
|
57 | underlined(_("COMMANDS")) | |||
|
58 | h = {} | |||
|
59 | for c, attr in table.items(): | |||
|
60 | f = c.split("|")[0] | |||
|
61 | f = f.lstrip("^") | |||
|
62 | h[f] = c | |||
|
63 | cmds = h.keys() | |||
|
64 | cmds.sort() | |||
|
65 | ||||
|
66 | for f in cmds: | |||
|
67 | if f.startswith("debug"): continue | |||
|
68 | d = get_cmd(h[f]) | |||
|
69 | # synopsis | |||
|
70 | ui.write("%s::\n" % d['synopsis'].replace("hg ","", 1)) | |||
|
71 | # description | |||
|
72 | ui.write("%s\n\n" % d['desc'][1]) | |||
|
73 | # options | |||
|
74 | opt_output = list(d['opts']) | |||
|
75 | if opt_output: | |||
|
76 | opts_len = max([len(line[0]) for line in opt_output]) | |||
|
77 | ui.write(_(" options:\n")) | |||
|
78 | for optstr, desc in opt_output: | |||
|
79 | if desc: | |||
|
80 | s = "%-*s %s" % (opts_len, optstr, desc) | |||
|
81 | else: | |||
|
82 | s = optstr | |||
|
83 | s = textwrap.fill(s, initial_indent=4 * " ", | |||
|
84 | subsequent_indent=(6 + opts_len) * " ") | |||
|
85 | ui.write("%s\n" % s) | |||
|
86 | ui.write("\n") | |||
|
87 | # aliases | |||
|
88 | if d['aliases']: | |||
|
89 | ui.write(_(" aliases: %s\n\n") % " ".join(d['aliases'])) | |||
|
90 | ||||
|
91 | if __name__ == "__main__": | |||
|
92 | show_doc(sys.stdout) |
This diff has been collapsed as it changes many lines, (1306 lines changed) Show them Hide them | |||||
@@ -0,0 +1,1306 b'' | |||||
|
1 | # queue.py - patch queues for mercurial | |||
|
2 | # | |||
|
3 | # Copyright 2005 Chris Mason <mason@suse.com> | |||
|
4 | # | |||
|
5 | # This software may be used and distributed according to the terms | |||
|
6 | # of the GNU General Public License, incorporated herein by reference. | |||
|
7 | ||||
|
8 | from mercurial.demandload import * | |||
|
9 | demandload(globals(), "os sys re struct traceback errno bz2") | |||
|
10 | from mercurial.i18n import gettext as _ | |||
|
11 | from mercurial import ui, hg, revlog, commands, util | |||
|
12 | ||||
|
13 | versionstr = "0.45" | |||
|
14 | ||||
|
15 | repomap = {} | |||
|
16 | ||||
|
17 | class queue: | |||
|
18 | def __init__(self, ui, path, patchdir=None): | |||
|
19 | self.basepath = path | |||
|
20 | if patchdir: | |||
|
21 | self.path = patchdir | |||
|
22 | else: | |||
|
23 | self.path = os.path.join(path, "patches") | |||
|
24 | self.opener = util.opener(self.path) | |||
|
25 | self.ui = ui | |||
|
26 | self.applied = [] | |||
|
27 | self.full_series = [] | |||
|
28 | self.applied_dirty = 0 | |||
|
29 | self.series_dirty = 0 | |||
|
30 | self.series_path = "series" | |||
|
31 | self.status_path = "status" | |||
|
32 | ||||
|
33 | if os.path.exists(os.path.join(self.path, self.series_path)): | |||
|
34 | self.full_series = self.opener(self.series_path).read().splitlines() | |||
|
35 | self.read_series(self.full_series) | |||
|
36 | ||||
|
37 | if os.path.exists(os.path.join(self.path, self.status_path)): | |||
|
38 | self.applied = self.opener(self.status_path).read().splitlines() | |||
|
39 | ||||
|
40 | def find_series(self, patch): | |||
|
41 | pre = re.compile("(\s*)([^#]+)") | |||
|
42 | index = 0 | |||
|
43 | for l in self.full_series: | |||
|
44 | m = pre.match(l) | |||
|
45 | if m: | |||
|
46 | s = m.group(2) | |||
|
47 | s = s.rstrip() | |||
|
48 | if s == patch: | |||
|
49 | return index | |||
|
50 | index += 1 | |||
|
51 | return None | |||
|
52 | ||||
|
53 | def read_series(self, list): | |||
|
54 | def matcher(list): | |||
|
55 | pre = re.compile("(\s*)([^#]+)") | |||
|
56 | for l in list: | |||
|
57 | m = pre.match(l) | |||
|
58 | if m: | |||
|
59 | s = m.group(2) | |||
|
60 | s = s.rstrip() | |||
|
61 | if len(s) > 0: | |||
|
62 | yield s | |||
|
63 | self.series = [] | |||
|
64 | self.series = [ x for x in matcher(list) ] | |||
|
65 | ||||
|
66 | def save_dirty(self): | |||
|
67 | if self.applied_dirty: | |||
|
68 | if len(self.applied) > 0: | |||
|
69 | nl = "\n" | |||
|
70 | else: | |||
|
71 | nl = "" | |||
|
72 | f = self.opener(self.status_path, "w") | |||
|
73 | f.write("\n".join(self.applied) + nl) | |||
|
74 | if self.series_dirty: | |||
|
75 | if len(self.full_series) > 0: | |||
|
76 | nl = "\n" | |||
|
77 | else: | |||
|
78 | nl = "" | |||
|
79 | f = self.opener(self.series_path, "w") | |||
|
80 | f.write("\n".join(self.full_series) + nl) | |||
|
81 | ||||
|
82 | def readheaders(self, patch): | |||
|
83 | def eatdiff(lines): | |||
|
84 | while lines: | |||
|
85 | l = lines[-1] | |||
|
86 | if (l.startswith("diff -") or | |||
|
87 | l.startswith("Index:") or | |||
|
88 | l.startswith("===========")): | |||
|
89 | del lines[-1] | |||
|
90 | else: | |||
|
91 | break | |||
|
92 | def eatempty(lines): | |||
|
93 | while lines: | |||
|
94 | l = lines[-1] | |||
|
95 | if re.match('\s*$', l): | |||
|
96 | del lines[-1] | |||
|
97 | else: | |||
|
98 | break | |||
|
99 | ||||
|
100 | pf = os.path.join(self.path, patch) | |||
|
101 | message = [] | |||
|
102 | comments = [] | |||
|
103 | user = None | |||
|
104 | format = None | |||
|
105 | subject = None | |||
|
106 | diffstart = 0 | |||
|
107 | ||||
|
108 | for line in file(pf): | |||
|
109 | line = line.rstrip() | |||
|
110 | if diffstart: | |||
|
111 | if line.startswith('+++ '): | |||
|
112 | diffstart = 2 | |||
|
113 | break | |||
|
114 | if line.startswith("--- "): | |||
|
115 | diffstart = 1 | |||
|
116 | continue | |||
|
117 | elif format == "hgpatch": | |||
|
118 | # parse values when importing the result of an hg export | |||
|
119 | if line.startswith("# User "): | |||
|
120 | user = line[7:] | |||
|
121 | elif not line.startswith("# ") and line: | |||
|
122 | message.append(line) | |||
|
123 | format = None | |||
|
124 | elif line == '# HG changeset patch': | |||
|
125 | format = "hgpatch" | |||
|
126 | elif (format != "tagdone" and (line.startswith("Subject: ") or | |||
|
127 | line.startswith("subject: "))): | |||
|
128 | subject = line[9:] | |||
|
129 | format = "tag" | |||
|
130 | elif (format != "tagdone" and (line.startswith("From: ") or | |||
|
131 | line.startswith("from: "))): | |||
|
132 | user = line[6:] | |||
|
133 | format = "tag" | |||
|
134 | elif format == "tag" and line == "": | |||
|
135 | # when looking for tags (subject: from: etc) they | |||
|
136 | # end once you find a blank line in the source | |||
|
137 | format = "tagdone" | |||
|
138 | else: | |||
|
139 | message.append(line) | |||
|
140 | comments.append(line) | |||
|
141 | ||||
|
142 | eatdiff(message) | |||
|
143 | eatdiff(comments) | |||
|
144 | eatempty(message) | |||
|
145 | eatempty(comments) | |||
|
146 | ||||
|
147 | # make sure message isn't empty | |||
|
148 | if format and format.startswith("tag") and subject: | |||
|
149 | message.insert(0, "") | |||
|
150 | message.insert(0, subject) | |||
|
151 | return (message, comments, user, diffstart > 1) | |||
|
152 | ||||
|
153 | def mergeone(self, repo, mergeq, head, patch, rev, wlock): | |||
|
154 | # first try just applying the patch | |||
|
155 | (err, n) = self.apply(repo, [ patch ], update_status=False, | |||
|
156 | strict=True, merge=rev, wlock=wlock) | |||
|
157 | ||||
|
158 | if err == 0: | |||
|
159 | return (err, n) | |||
|
160 | ||||
|
161 | if n is None: | |||
|
162 | self.ui.warn("apply failed for patch %s\n" % patch) | |||
|
163 | sys.exit(1) | |||
|
164 | ||||
|
165 | self.ui.warn("patch didn't work out, merging %s\n" % patch) | |||
|
166 | ||||
|
167 | # apply failed, strip away that rev and merge. | |||
|
168 | repo.update(head, allow=False, force=True, wlock=wlock) | |||
|
169 | self.strip(repo, n, update=False, backup='strip', wlock=wlock) | |||
|
170 | ||||
|
171 | c = repo.changelog.read(rev) | |||
|
172 | ret = repo.update(rev, allow=True, wlock=wlock) | |||
|
173 | if ret: | |||
|
174 | self.ui.warn("update returned %d\n" % ret) | |||
|
175 | sys.exit(1) | |||
|
176 | n = repo.commit(None, c[4], c[1], force=1, wlock=wlock) | |||
|
177 | if n == None: | |||
|
178 | self.ui.warn("repo commit failed\n") | |||
|
179 | sys.exit(1) | |||
|
180 | try: | |||
|
181 | message, comments, user, patchfound = mergeq.readheaders(patch) | |||
|
182 | except: | |||
|
183 | self.ui.warn("Unable to read %s\n" % patch) | |||
|
184 | sys.exit(1) | |||
|
185 | ||||
|
186 | patchf = self.opener(patch, "w") | |||
|
187 | if comments: | |||
|
188 | comments = "\n".join(comments) + '\n\n' | |||
|
189 | patchf.write(comments) | |||
|
190 | commands.dodiff(patchf, self.ui, repo, head, n) | |||
|
191 | patchf.close() | |||
|
192 | return (0, n) | |||
|
193 | ||||
|
194 | def qparents(self, repo, rev=None): | |||
|
195 | if rev is None: | |||
|
196 | (p1, p2) = repo.dirstate.parents() | |||
|
197 | if p2 == revlog.nullid: | |||
|
198 | return p1 | |||
|
199 | if len(self.applied) == 0: | |||
|
200 | return None | |||
|
201 | (top, patch) = self.applied[-1].split(':') | |||
|
202 | top = revlog.bin(top) | |||
|
203 | return top | |||
|
204 | pp = repo.changelog.parents(rev) | |||
|
205 | if pp[1] != revlog.nullid: | |||
|
206 | arevs = [ x.split(':')[0] for x in self.applied ] | |||
|
207 | p0 = revlog.hex(pp[0]) | |||
|
208 | p1 = revlog.hex(pp[1]) | |||
|
209 | if p0 in arevs: | |||
|
210 | return pp[0] | |||
|
211 | if p1 in arevs: | |||
|
212 | return pp[1] | |||
|
213 | return None | |||
|
214 | return pp[0] | |||
|
215 | ||||
|
216 | def mergepatch(self, repo, mergeq, series, wlock): | |||
|
217 | if len(self.applied) == 0: | |||
|
218 | # each of the patches merged in will have two parents. This | |||
|
219 | # can confuse the qrefresh, qdiff, and strip code because it | |||
|
220 | # needs to know which parent is actually in the patch queue. | |||
|
221 | # so, we insert a merge marker with only one parent. This way | |||
|
222 | # the first patch in the queue is never a merge patch | |||
|
223 | # | |||
|
224 | pname = ".hg.patches.merge.marker" | |||
|
225 | n = repo.commit(None, '[mq]: merge marker', user=None, force=1, | |||
|
226 | wlock=wlock) | |||
|
227 | self.applied.append(revlog.hex(n) + ":" + pname) | |||
|
228 | self.applied_dirty = 1 | |||
|
229 | ||||
|
230 | head = self.qparents(repo) | |||
|
231 | ||||
|
232 | for patch in series: | |||
|
233 | patch = mergeq.lookup(patch) | |||
|
234 | if not patch: | |||
|
235 | self.ui.warn("patch %s does not exist\n" % patch) | |||
|
236 | return (1, None) | |||
|
237 | ||||
|
238 | info = mergeq.isapplied(patch) | |||
|
239 | if not info: | |||
|
240 | self.ui.warn("patch %s is not applied\n" % patch) | |||
|
241 | return (1, None) | |||
|
242 | rev = revlog.bin(info[1]) | |||
|
243 | (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock) | |||
|
244 | if head: | |||
|
245 | self.applied.append(revlog.hex(head) + ":" + patch) | |||
|
246 | self.applied_dirty = 1 | |||
|
247 | if err: | |||
|
248 | return (err, head) | |||
|
249 | return (0, head) | |||
|
250 | ||||
|
251 | def apply(self, repo, series, list=False, update_status=True, | |||
|
252 | strict=False, patchdir=None, merge=None, wlock=None): | |||
|
253 | # TODO unify with commands.py | |||
|
254 | if not patchdir: | |||
|
255 | patchdir = self.path | |||
|
256 | pwd = os.getcwd() | |||
|
257 | os.chdir(repo.root) | |||
|
258 | err = 0 | |||
|
259 | if not wlock: | |||
|
260 | wlock = repo.wlock() | |||
|
261 | lock = repo.lock() | |||
|
262 | tr = repo.transaction() | |||
|
263 | n = None | |||
|
264 | for patch in series: | |||
|
265 | self.ui.warn("applying %s\n" % patch) | |||
|
266 | pf = os.path.join(patchdir, patch) | |||
|
267 | ||||
|
268 | try: | |||
|
269 | message, comments, user, patchfound = self.readheaders(patch) | |||
|
270 | except: | |||
|
271 | self.ui.warn("Unable to read %s\n" % pf) | |||
|
272 | err = 1 | |||
|
273 | break | |||
|
274 | ||||
|
275 | if not message: | |||
|
276 | message = "imported patch %s\n" % patch | |||
|
277 | else: | |||
|
278 | if list: | |||
|
279 | message.append("\nimported patch %s" % patch) | |||
|
280 | message = '\n'.join(message) | |||
|
281 | ||||
|
282 | try: | |||
|
283 | f = os.popen("patch -p1 --no-backup-if-mismatch < '%s'" % (pf)) | |||
|
284 | except: | |||
|
285 | self.ui.warn("patch failed, unable to continue (try -v)\n") | |||
|
286 | err = 1 | |||
|
287 | break | |||
|
288 | files = [] | |||
|
289 | fuzz = False | |||
|
290 | for l in f: | |||
|
291 | l = l.rstrip('\r\n'); | |||
|
292 | if self.ui.verbose: | |||
|
293 | self.ui.warn(l + "\n") | |||
|
294 | if l[:14] == 'patching file ': | |||
|
295 | pf = os.path.normpath(l[14:]) | |||
|
296 | # when patch finds a space in the file name, it puts | |||
|
297 | # single quotes around the filename. strip them off | |||
|
298 | if pf[0] == "'" and pf[-1] == "'": | |||
|
299 | pf = pf[1:-1] | |||
|
300 | if pf not in files: | |||
|
301 | files.append(pf) | |||
|
302 | printed_file = False | |||
|
303 | file_str = l | |||
|
304 | elif l.find('with fuzz') >= 0: | |||
|
305 | if not printed_file: | |||
|
306 | self.ui.warn(file_str + '\n') | |||
|
307 | printed_file = True | |||
|
308 | self.ui.warn(l + '\n') | |||
|
309 | fuzz = True | |||
|
310 | elif l.find('saving rejects to file') >= 0: | |||
|
311 | self.ui.warn(l + '\n') | |||
|
312 | elif l.find('FAILED') >= 0: | |||
|
313 | if not printed_file: | |||
|
314 | self.ui.warn(file_str + '\n') | |||
|
315 | printed_file = True | |||
|
316 | self.ui.warn(l + '\n') | |||
|
317 | patcherr = f.close() | |||
|
318 | ||||
|
319 | if merge and len(files) > 0: | |||
|
320 | # Mark as merged and update dirstate parent info | |||
|
321 | repo.dirstate.update(repo.dirstate.filterfiles(files), 'm') | |||
|
322 | p1, p2 = repo.dirstate.parents() | |||
|
323 | repo.dirstate.setparents(p1, merge) | |||
|
324 | if len(files) > 0: | |||
|
325 | commands.addremove_lock(self.ui, repo, files, | |||
|
326 | opts={}, wlock=wlock) | |||
|
327 | n = repo.commit(files, message, user, force=1, lock=lock, | |||
|
328 | wlock=wlock) | |||
|
329 | ||||
|
330 | if n == None: | |||
|
331 | self.ui.warn("repo commit failed\n") | |||
|
332 | sys.exit(1) | |||
|
333 | ||||
|
334 | if update_status: | |||
|
335 | self.applied.append(revlog.hex(n) + ":" + patch) | |||
|
336 | ||||
|
337 | if patcherr: | |||
|
338 | if not patchfound: | |||
|
339 | self.ui.warn("patch %s is empty\n" % patch) | |||
|
340 | err = 0 | |||
|
341 | else: | |||
|
342 | self.ui.warn("patch failed, rejects left in working dir\n") | |||
|
343 | err = 1 | |||
|
344 | break | |||
|
345 | ||||
|
346 | if fuzz and strict: | |||
|
347 | self.ui.warn("fuzz found when applying patch, stopping\n") | |||
|
348 | err = 1 | |||
|
349 | break | |||
|
350 | tr.close() | |||
|
351 | os.chdir(pwd) | |||
|
352 | return (err, n) | |||
|
353 | ||||
|
354 | def delete(self, repo, patch): | |||
|
355 | patch = self.lookup(patch) | |||
|
356 | info = self.isapplied(patch) | |||
|
357 | if info: | |||
|
358 | self.ui.warn("cannot delete applied patch %s\n" % patch) | |||
|
359 | sys.exit(1) | |||
|
360 | if patch not in self.series: | |||
|
361 | self.ui.warn("patch %s not in series file\n" % patch) | |||
|
362 | sys.exit(1) | |||
|
363 | i = self.find_series(patch) | |||
|
364 | del self.full_series[i] | |||
|
365 | self.read_series(self.full_series) | |||
|
366 | self.series_dirty = 1 | |||
|
367 | ||||
|
368 | def check_toppatch(self, repo): | |||
|
369 | if len(self.applied) > 0: | |||
|
370 | (top, patch) = self.applied[-1].split(':') | |||
|
371 | top = revlog.bin(top) | |||
|
372 | pp = repo.dirstate.parents() | |||
|
373 | if top not in pp: | |||
|
374 | self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1]))) | |||
|
375 | sys.exit(1) | |||
|
376 | return top | |||
|
377 | return None | |||
|
378 | def check_localchanges(self, repo): | |||
|
379 | (c, a, r, d, u) = repo.changes(None, None) | |||
|
380 | if c or a or d or r: | |||
|
381 | self.ui.write("Local changes found, refresh first\n") | |||
|
382 | sys.exit(1) | |||
|
383 | def new(self, repo, patch, msg=None, force=None): | |||
|
384 | if not force: | |||
|
385 | self.check_localchanges(repo) | |||
|
386 | self.check_toppatch(repo) | |||
|
387 | wlock = repo.wlock() | |||
|
388 | insert = self.series_end() | |||
|
389 | if msg: | |||
|
390 | n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock) | |||
|
391 | else: | |||
|
392 | n = repo.commit([], | |||
|
393 | "New patch: %s" % patch, force=True, wlock=wlock) | |||
|
394 | if n == None: | |||
|
395 | self.ui.warn("repo commit failed\n") | |||
|
396 | sys.exit(1) | |||
|
397 | self.full_series[insert:insert] = [patch] | |||
|
398 | self.applied.append(revlog.hex(n) + ":" + patch) | |||
|
399 | self.read_series(self.full_series) | |||
|
400 | self.series_dirty = 1 | |||
|
401 | self.applied_dirty = 1 | |||
|
402 | p = self.opener(patch, "w") | |||
|
403 | if msg: | |||
|
404 | msg = msg + "\n" | |||
|
405 | p.write(msg) | |||
|
406 | p.close() | |||
|
407 | wlock = None | |||
|
408 | r = self.qrepo() | |||
|
409 | if r: r.add([patch]) | |||
|
410 | ||||
|
411 | def strip(self, repo, rev, update=True, backup="all", wlock=None): | |||
|
412 | def limitheads(chlog, stop): | |||
|
413 | """return the list of all nodes that have no children""" | |||
|
414 | p = {} | |||
|
415 | h = [] | |||
|
416 | stoprev = 0 | |||
|
417 | if stop in chlog.nodemap: | |||
|
418 | stoprev = chlog.rev(stop) | |||
|
419 | ||||
|
420 | for r in range(chlog.count() - 1, -1, -1): | |||
|
421 | n = chlog.node(r) | |||
|
422 | if n not in p: | |||
|
423 | h.append(n) | |||
|
424 | if n == stop: | |||
|
425 | break | |||
|
426 | if r < stoprev: | |||
|
427 | break | |||
|
428 | for pn in chlog.parents(n): | |||
|
429 | p[pn] = 1 | |||
|
430 | return h | |||
|
431 | ||||
|
432 | def bundle(cg): | |||
|
433 | backupdir = repo.join("strip-backup") | |||
|
434 | if not os.path.isdir(backupdir): | |||
|
435 | os.mkdir(backupdir) | |||
|
436 | name = os.path.join(backupdir, "%s" % revlog.short(rev)) | |||
|
437 | name = savename(name) | |||
|
438 | self.ui.warn("saving bundle to %s\n" % name) | |||
|
439 | # TODO, exclusive open | |||
|
440 | f = open(name, "wb") | |||
|
441 | try: | |||
|
442 | f.write("HG10") | |||
|
443 | z = bz2.BZ2Compressor(9) | |||
|
444 | while 1: | |||
|
445 | chunk = cg.read(4096) | |||
|
446 | if not chunk: | |||
|
447 | break | |||
|
448 | f.write(z.compress(chunk)) | |||
|
449 | f.write(z.flush()) | |||
|
450 | except: | |||
|
451 | os.unlink(name) | |||
|
452 | raise | |||
|
453 | f.close() | |||
|
454 | return name | |||
|
455 | ||||
|
456 | def stripall(rev, revnum): | |||
|
457 | cl = repo.changelog | |||
|
458 | c = cl.read(rev) | |||
|
459 | mm = repo.manifest.read(c[0]) | |||
|
460 | seen = {} | |||
|
461 | ||||
|
462 | for x in xrange(revnum, cl.count()): | |||
|
463 | c = cl.read(cl.node(x)) | |||
|
464 | for f in c[3]: | |||
|
465 | if f in seen: | |||
|
466 | continue | |||
|
467 | seen[f] = 1 | |||
|
468 | if f in mm: | |||
|
469 | filerev = mm[f] | |||
|
470 | else: | |||
|
471 | filerev = 0 | |||
|
472 | seen[f] = filerev | |||
|
473 | # we go in two steps here so the strip loop happens in a | |||
|
474 | # sensible order. When stripping many files, this helps keep | |||
|
475 | # our disk access patterns under control. | |||
|
476 | list = seen.keys() | |||
|
477 | list.sort() | |||
|
478 | for f in list: | |||
|
479 | ff = repo.file(f) | |||
|
480 | filerev = seen[f] | |||
|
481 | if filerev != 0: | |||
|
482 | if filerev in ff.nodemap: | |||
|
483 | filerev = ff.rev(filerev) | |||
|
484 | else: | |||
|
485 | filerev = 0 | |||
|
486 | ff.strip(filerev, revnum) | |||
|
487 | ||||
|
488 | if not wlock: | |||
|
489 | wlock = repo.wlock() | |||
|
490 | lock = repo.lock() | |||
|
491 | chlog = repo.changelog | |||
|
492 | # TODO delete the undo files, and handle undo of merge sets | |||
|
493 | pp = chlog.parents(rev) | |||
|
494 | revnum = chlog.rev(rev) | |||
|
495 | ||||
|
496 | if update: | |||
|
497 | urev = self.qparents(repo, rev) | |||
|
498 | repo.update(urev, allow=False, force=True, wlock=wlock) | |||
|
499 | repo.dirstate.write() | |||
|
500 | ||||
|
501 | # save is a list of all the branches we are truncating away | |||
|
502 | # that we actually want to keep. changegroup will be used | |||
|
503 | # to preserve them and add them back after the truncate | |||
|
504 | saveheads = [] | |||
|
505 | savebases = {} | |||
|
506 | ||||
|
507 | tip = chlog.tip() | |||
|
508 | heads = limitheads(chlog, rev) | |||
|
509 | seen = {} | |||
|
510 | ||||
|
511 | # search through all the heads, finding those where the revision | |||
|
512 | # we want to strip away is an ancestor. Also look for merges | |||
|
513 | # that might be turned into new heads by the strip. | |||
|
514 | while heads: | |||
|
515 | h = heads.pop() | |||
|
516 | n = h | |||
|
517 | while True: | |||
|
518 | seen[n] = 1 | |||
|
519 | pp = chlog.parents(n) | |||
|
520 | if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum: | |||
|
521 | if pp[1] not in seen: | |||
|
522 | heads.append(pp[1]) | |||
|
523 | if pp[0] == revlog.nullid: | |||
|
524 | break | |||
|
525 | if chlog.rev(pp[0]) < revnum: | |||
|
526 | break | |||
|
527 | n = pp[0] | |||
|
528 | if n == rev: | |||
|
529 | break | |||
|
530 | r = chlog.reachable(h, rev) | |||
|
531 | if rev not in r: | |||
|
532 | saveheads.append(h) | |||
|
533 | for x in r: | |||
|
534 | if chlog.rev(x) > revnum: | |||
|
535 | savebases[x] = 1 | |||
|
536 | ||||
|
537 | # create a changegroup for all the branches we need to keep | |||
|
538 | if backup is "all": | |||
|
539 | backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip') | |||
|
540 | bundle(backupch) | |||
|
541 | if saveheads: | |||
|
542 | backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip') | |||
|
543 | chgrpfile = bundle(backupch) | |||
|
544 | ||||
|
545 | stripall(rev, revnum) | |||
|
546 | ||||
|
547 | change = chlog.read(rev) | |||
|
548 | repo.manifest.strip(repo.manifest.rev(change[0]), revnum) | |||
|
549 | chlog.strip(revnum, revnum) | |||
|
550 | if saveheads: | |||
|
551 | self.ui.status("adding branch\n") | |||
|
552 | commands.unbundle(self.ui, repo, chgrpfile, update=False) | |||
|
553 | if backup is not "strip": | |||
|
554 | os.unlink(chgrpfile) | |||
|
555 | ||||
|
556 | def isapplied(self, patch): | |||
|
557 | """returns (index, rev, patch)""" | |||
|
558 | for i in xrange(len(self.applied)): | |||
|
559 | p = self.applied[i] | |||
|
560 | a = p.split(':') | |||
|
561 | if a[1] == patch: | |||
|
562 | return (i, a[0], a[1]) | |||
|
563 | return None | |||
|
564 | ||||
|
565 | def lookup(self, patch): | |||
|
566 | if patch == None: | |||
|
567 | return None | |||
|
568 | if patch in self.series: | |||
|
569 | return patch | |||
|
570 | if not os.path.isfile(os.path.join(self.path, patch)): | |||
|
571 | try: | |||
|
572 | sno = int(patch) | |||
|
573 | except(ValueError, OverflowError): | |||
|
574 | self.ui.warn("patch %s not in series\n" % patch) | |||
|
575 | sys.exit(1) | |||
|
576 | if sno >= len(self.series): | |||
|
577 | self.ui.warn("patch number %d is out of range\n" % sno) | |||
|
578 | sys.exit(1) | |||
|
579 | patch = self.series[sno] | |||
|
580 | else: | |||
|
581 | self.ui.warn("patch %s not in series\n" % patch) | |||
|
582 | sys.exit(1) | |||
|
583 | return patch | |||
|
584 | ||||
|
585 | def push(self, repo, patch=None, force=False, list=False, | |||
|
586 | mergeq=None, wlock=None): | |||
|
587 | if not wlock: | |||
|
588 | wlock = repo.wlock() | |||
|
589 | patch = self.lookup(patch) | |||
|
590 | if patch and self.isapplied(patch): | |||
|
591 | self.ui.warn("patch %s is already applied\n" % patch) | |||
|
592 | sys.exit(1) | |||
|
593 | if self.series_end() == len(self.series): | |||
|
594 | self.ui.warn("File series fully applied\n") | |||
|
595 | sys.exit(1) | |||
|
596 | if not force: | |||
|
597 | self.check_localchanges(repo) | |||
|
598 | ||||
|
599 | self.applied_dirty = 1; | |||
|
600 | start = self.series_end() | |||
|
601 | if start > 0: | |||
|
602 | self.check_toppatch(repo) | |||
|
603 | if not patch: | |||
|
604 | patch = self.series[start] | |||
|
605 | end = start + 1 | |||
|
606 | else: | |||
|
607 | end = self.series.index(patch, start) + 1 | |||
|
608 | s = self.series[start:end] | |||
|
609 | if mergeq: | |||
|
610 | ret = self.mergepatch(repo, mergeq, s, wlock) | |||
|
611 | else: | |||
|
612 | ret = self.apply(repo, s, list, wlock=wlock) | |||
|
613 | top = self.applied[-1].split(':')[1] | |||
|
614 | if ret[0]: | |||
|
615 | self.ui.write("Errors during apply, please fix and refresh %s\n" % | |||
|
616 | top) | |||
|
617 | else: | |||
|
618 | self.ui.write("Now at: %s\n" % top) | |||
|
619 | return ret[0] | |||
|
620 | ||||
|
621 | def pop(self, repo, patch=None, force=False, update=True, wlock=None): | |||
|
622 | def getfile(f, rev): | |||
|
623 | t = repo.file(f).read(rev) | |||
|
624 | try: | |||
|
625 | repo.wfile(f, "w").write(t) | |||
|
626 | except IOError: | |||
|
627 | os.makedirs(os.path.dirname(repo.wjoin(f))) | |||
|
628 | repo.wfile(f, "w").write(t) | |||
|
629 | ||||
|
630 | if not wlock: | |||
|
631 | wlock = repo.wlock() | |||
|
632 | if patch: | |||
|
633 | # index, rev, patch | |||
|
634 | info = self.isapplied(patch) | |||
|
635 | if not info: | |||
|
636 | patch = self.lookup(patch) | |||
|
637 | info = self.isapplied(patch) | |||
|
638 | if not info: | |||
|
639 | self.ui.warn("patch %s is not applied\n" % patch) | |||
|
640 | sys.exit(1) | |||
|
641 | if len(self.applied) == 0: | |||
|
642 | self.ui.warn("No patches applied\n") | |||
|
643 | sys.exit(1) | |||
|
644 | ||||
|
645 | if not update: | |||
|
646 | parents = repo.dirstate.parents() | |||
|
647 | rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ] | |||
|
648 | for p in parents: | |||
|
649 | if p in rr: | |||
|
650 | self.ui.warn("qpop: forcing dirstate update\n") | |||
|
651 | update = True | |||
|
652 | ||||
|
653 | if not force and update: | |||
|
654 | self.check_localchanges(repo) | |||
|
655 | ||||
|
656 | self.applied_dirty = 1; | |||
|
657 | end = len(self.applied) | |||
|
658 | if not patch: | |||
|
659 | info = [len(self.applied) - 1] + self.applied[-1].split(':') | |||
|
660 | start = info[0] | |||
|
661 | rev = revlog.bin(info[1]) | |||
|
662 | ||||
|
663 | # we know there are no local changes, so we can make a simplified | |||
|
664 | # form of hg.update. | |||
|
665 | if update: | |||
|
666 | top = self.check_toppatch(repo) | |||
|
667 | qp = self.qparents(repo, rev) | |||
|
668 | changes = repo.changelog.read(qp) | |||
|
669 | mf1 = repo.manifest.readflags(changes[0]) | |||
|
670 | mmap = repo.manifest.read(changes[0]) | |||
|
671 | (c, a, r, d, u) = repo.changes(qp, top) | |||
|
672 | if d: | |||
|
673 | raise util.Abort("deletions found between repo revs") | |||
|
674 | for f in c: | |||
|
675 | getfile(f, mmap[f]) | |||
|
676 | for f in r: | |||
|
677 | getfile(f, mmap[f]) | |||
|
678 | util.set_exec(repo.wjoin(f), mf1[f]) | |||
|
679 | repo.dirstate.update(c + r, 'n') | |||
|
680 | for f in a: | |||
|
681 | try: os.unlink(repo.wjoin(f)) | |||
|
682 | except: raise | |||
|
683 | try: os.removedirs(os.path.dirname(repo.wjoin(f))) | |||
|
684 | except: pass | |||
|
685 | if a: | |||
|
686 | repo.dirstate.forget(a) | |||
|
687 | repo.dirstate.setparents(qp, revlog.nullid) | |||
|
688 | self.strip(repo, rev, update=False, backup='strip', wlock=wlock) | |||
|
689 | del self.applied[start:end] | |||
|
690 | if len(self.applied): | |||
|
691 | self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1]) | |||
|
692 | else: | |||
|
693 | self.ui.write("Patch queue now empty\n") | |||
|
694 | ||||
|
695 | def diff(self, repo, files): | |||
|
696 | top = self.check_toppatch(repo) | |||
|
697 | if not top: | |||
|
698 | self.ui.write("No patches applied\n") | |||
|
699 | return | |||
|
700 | qp = self.qparents(repo, top) | |||
|
701 | commands.dodiff(sys.stdout, self.ui, repo, qp, None, files) | |||
|
702 | ||||
|
703 | def refresh(self, repo, short=False): | |||
|
704 | if len(self.applied) == 0: | |||
|
705 | self.ui.write("No patches applied\n") | |||
|
706 | return | |||
|
707 | wlock = repo.wlock() | |||
|
708 | self.check_toppatch(repo) | |||
|
709 | qp = self.qparents(repo) | |||
|
710 | (top, patch) = self.applied[-1].split(':') | |||
|
711 | top = revlog.bin(top) | |||
|
712 | cparents = repo.changelog.parents(top) | |||
|
713 | patchparent = self.qparents(repo, top) | |||
|
714 | message, comments, user, patchfound = self.readheaders(patch) | |||
|
715 | ||||
|
716 | patchf = self.opener(patch, "w") | |||
|
717 | if comments: | |||
|
718 | comments = "\n".join(comments) + '\n\n' | |||
|
719 | patchf.write(comments) | |||
|
720 | ||||
|
721 | tip = repo.changelog.tip() | |||
|
722 | if top == tip: | |||
|
723 | # if the top of our patch queue is also the tip, there is an | |||
|
724 | # optimization here. We update the dirstate in place and strip | |||
|
725 | # off the tip commit. Then just commit the current directory | |||
|
726 | # tree. We can also send repo.commit the list of files | |||
|
727 | # changed to speed up the diff | |||
|
728 | # | |||
|
729 | # in short mode, we only diff the files included in the | |||
|
730 | # patch already | |||
|
731 | # | |||
|
732 | # this should really read: | |||
|
733 | #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent) | |||
|
734 | # but we do it backwards to take advantage of manifest/chlog | |||
|
735 | # caching against the next repo.changes call | |||
|
736 | # | |||
|
737 | (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip) | |||
|
738 | if short: | |||
|
739 | filelist = cc + aa + dd | |||
|
740 | else: | |||
|
741 | filelist = None | |||
|
742 | (c, a, r, d, u) = repo.changes(None, None, filelist) | |||
|
743 | ||||
|
744 | # we might end up with files that were added between tip and | |||
|
745 | # the dirstate parent, but then changed in the local dirstate. | |||
|
746 | # in this case, we want them to only show up in the added section | |||
|
747 | for x in c: | |||
|
748 | if x not in aa: | |||
|
749 | cc.append(x) | |||
|
750 | # we might end up with files added by the local dirstate that | |||
|
751 | # were deleted by the patch. In this case, they should only | |||
|
752 | # show up in the changed section. | |||
|
753 | for x in a: | |||
|
754 | if x in dd: | |||
|
755 | del dd[dd.index(x)] | |||
|
756 | cc.append(x) | |||
|
757 | else: | |||
|
758 | aa.append(x) | |||
|
759 | # make sure any files deleted in the local dirstate | |||
|
760 | # are not in the add or change column of the patch | |||
|
761 | forget = [] | |||
|
762 | for x in d + r: | |||
|
763 | if x in aa: | |||
|
764 | del aa[aa.index(x)] | |||
|
765 | forget.append(x) | |||
|
766 | continue | |||
|
767 | elif x in cc: | |||
|
768 | del cc[cc.index(x)] | |||
|
769 | dd.append(x) | |||
|
770 | ||||
|
771 | c = list(util.unique(cc)) | |||
|
772 | r = list(util.unique(dd)) | |||
|
773 | a = list(util.unique(aa)) | |||
|
774 | filelist = list(util.unique(c + r + a )) | |||
|
775 | commands.dodiff(patchf, self.ui, repo, patchparent, None, | |||
|
776 | filelist, changes=(c, a, r, [], u)) | |||
|
777 | patchf.close() | |||
|
778 | ||||
|
779 | changes = repo.changelog.read(tip) | |||
|
780 | repo.dirstate.setparents(*cparents) | |||
|
781 | repo.dirstate.update(a, 'a') | |||
|
782 | repo.dirstate.update(r, 'r') | |||
|
783 | repo.dirstate.update(c, 'n') | |||
|
784 | repo.dirstate.forget(forget) | |||
|
785 | ||||
|
786 | if not message: | |||
|
787 | message = "patch queue: %s\n" % patch | |||
|
788 | else: | |||
|
789 | message = "\n".join(message) | |||
|
790 | self.strip(repo, top, update=False, backup='strip', wlock=wlock) | |||
|
791 | n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock) | |||
|
792 | self.applied[-1] = revlog.hex(n) + ':' + patch | |||
|
793 | self.applied_dirty = 1 | |||
|
794 | else: | |||
|
795 | commands.dodiff(patchf, self.ui, repo, patchparent, None) | |||
|
796 | patchf.close() | |||
|
797 | self.pop(repo, force=True, wlock=wlock) | |||
|
798 | self.push(repo, force=True, wlock=wlock) | |||
|
799 | ||||
|
800 | def init(self, repo, create=False): | |||
|
801 | if os.path.isdir(self.path): | |||
|
802 | raise util.Abort("patch queue directory already exists") | |||
|
803 | os.mkdir(self.path) | |||
|
804 | if create: | |||
|
805 | return self.qrepo(create=True) | |||
|
806 | ||||
|
807 | def unapplied(self, repo, patch=None): | |||
|
808 | if patch and patch not in self.series: | |||
|
809 | self.ui.warn("%s not in the series file\n" % patch) | |||
|
810 | sys.exit(1) | |||
|
811 | if not patch: | |||
|
812 | start = self.series_end() | |||
|
813 | else: | |||
|
814 | start = self.series.index(patch) + 1 | |||
|
815 | for p in self.series[start:]: | |||
|
816 | self.ui.write("%s\n" % p) | |||
|
817 | ||||
|
818 | def qseries(self, repo, missing=None): | |||
|
819 | start = self.series_end() | |||
|
820 | if not missing: | |||
|
821 | for p in self.series[:start]: | |||
|
822 | if self.ui.verbose: | |||
|
823 | self.ui.write("%d A " % self.series.index(p)) | |||
|
824 | self.ui.write("%s\n" % p) | |||
|
825 | for p in self.series[start:]: | |||
|
826 | if self.ui.verbose: | |||
|
827 | self.ui.write("%d U " % self.series.index(p)) | |||
|
828 | self.ui.write("%s\n" % p) | |||
|
829 | else: | |||
|
830 | list = [] | |||
|
831 | for root, dirs, files in os.walk(self.path): | |||
|
832 | d = root[len(self.path) + 1:] | |||
|
833 | for f in files: | |||
|
834 | fl = os.path.join(d, f) | |||
|
835 | if (fl not in self.series and | |||
|
836 | fl not in (self.status_path, self.series_path) | |||
|
837 | and not fl.startswith('.')): | |||
|
838 | list.append(fl) | |||
|
839 | list.sort() | |||
|
840 | if list: | |||
|
841 | for x in list: | |||
|
842 | if self.ui.verbose: | |||
|
843 | self.ui.write("D ") | |||
|
844 | self.ui.write("%s\n" % x) | |||
|
845 | ||||
|
846 | def issaveline(self, l): | |||
|
847 | name = l.split(':')[1] | |||
|
848 | if name == '.hg.patches.save.line': | |||
|
849 | return True | |||
|
850 | ||||
|
851 | def qrepo(self, create=False): | |||
|
852 | if create or os.path.isdir(os.path.join(self.path, ".hg")): | |||
|
853 | return hg.repository(self.ui, path=self.path, create=create) | |||
|
854 | ||||
|
855 | def restore(self, repo, rev, delete=None, qupdate=None): | |||
|
856 | c = repo.changelog.read(rev) | |||
|
857 | desc = c[4].strip() | |||
|
858 | lines = desc.splitlines() | |||
|
859 | i = 0 | |||
|
860 | datastart = None | |||
|
861 | series = [] | |||
|
862 | applied = [] | |||
|
863 | qpp = None | |||
|
864 | for i in xrange(0, len(lines)): | |||
|
865 | if lines[i] == 'Patch Data:': | |||
|
866 | datastart = i + 1 | |||
|
867 | elif lines[i].startswith('Dirstate:'): | |||
|
868 | l = lines[i].rstrip() | |||
|
869 | l = l[10:].split(' ') | |||
|
870 | qpp = [ hg.bin(x) for x in l ] | |||
|
871 | elif datastart != None: | |||
|
872 | l = lines[i].rstrip() | |||
|
873 | index = l.index(':') | |||
|
874 | id = l[:index] | |||
|
875 | file = l[index + 1:] | |||
|
876 | if id: | |||
|
877 | applied.append(l) | |||
|
878 | series.append(file) | |||
|
879 | if datastart == None: | |||
|
880 | self.ui.warn("No saved patch data found\n") | |||
|
881 | return 1 | |||
|
882 | self.ui.warn("restoring status: %s\n" % lines[0]) | |||
|
883 | self.full_series = series | |||
|
884 | self.applied = applied | |||
|
885 | self.read_series(self.full_series) | |||
|
886 | self.series_dirty = 1 | |||
|
887 | self.applied_dirty = 1 | |||
|
888 | heads = repo.changelog.heads() | |||
|
889 | if delete: | |||
|
890 | if rev not in heads: | |||
|
891 | self.ui.warn("save entry has children, leaving it alone\n") | |||
|
892 | else: | |||
|
893 | self.ui.warn("removing save entry %s\n" % hg.short(rev)) | |||
|
894 | pp = repo.dirstate.parents() | |||
|
895 | if rev in pp: | |||
|
896 | update = True | |||
|
897 | else: | |||
|
898 | update = False | |||
|
899 | self.strip(repo, rev, update=update, backup='strip') | |||
|
900 | if qpp: | |||
|
901 | self.ui.warn("saved queue repository parents: %s %s\n" % | |||
|
902 | (hg.short(qpp[0]), hg.short(qpp[1]))) | |||
|
903 | if qupdate: | |||
|
904 | print "queue directory updating" | |||
|
905 | r = self.qrepo() | |||
|
906 | if not r: | |||
|
907 | self.ui.warn("Unable to load queue repository\n") | |||
|
908 | return 1 | |||
|
909 | r.update(qpp[0], allow=False, force=True) | |||
|
910 | ||||
|
911 | def save(self, repo, msg=None): | |||
|
912 | if len(self.applied) == 0: | |||
|
913 | self.ui.warn("save: no patches applied, exiting\n") | |||
|
914 | return 1 | |||
|
915 | if self.issaveline(self.applied[-1]): | |||
|
916 | self.ui.warn("status is already saved\n") | |||
|
917 | return 1 | |||
|
918 | ||||
|
919 | ar = [ ':' + x for x in self.full_series ] | |||
|
920 | if not msg: | |||
|
921 | msg = "hg patches saved state" | |||
|
922 | else: | |||
|
923 | msg = "hg patches: " + msg.rstrip('\r\n') | |||
|
924 | r = self.qrepo() | |||
|
925 | if r: | |||
|
926 | pp = r.dirstate.parents() | |||
|
927 | msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1])) | |||
|
928 | msg += "\n\nPatch Data:\n" | |||
|
929 | text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar) | |||
|
930 | + '\n' or "") | |||
|
931 | n = repo.commit(None, text, user=None, force=1) | |||
|
932 | if not n: | |||
|
933 | self.ui.warn("repo commit failed\n") | |||
|
934 | return 1 | |||
|
935 | self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line') | |||
|
936 | self.applied_dirty = 1 | |||
|
937 | ||||
|
938 | def series_end(self): | |||
|
939 | end = 0 | |||
|
940 | if len(self.applied) > 0: | |||
|
941 | (top, p) = self.applied[-1].split(':') | |||
|
942 | try: | |||
|
943 | end = self.series.index(p) | |||
|
944 | except ValueError: | |||
|
945 | return 0 | |||
|
946 | return end + 1 | |||
|
947 | return end | |||
|
948 | ||||
|
949 | def qapplied(self, repo, patch=None): | |||
|
950 | if patch and patch not in self.series: | |||
|
951 | self.ui.warn("%s not in the series file\n" % patch) | |||
|
952 | sys.exit(1) | |||
|
953 | if not patch: | |||
|
954 | end = len(self.applied) | |||
|
955 | else: | |||
|
956 | end = self.series.index(patch) + 1 | |||
|
957 | for x in xrange(end): | |||
|
958 | p = self.appliedname(x) | |||
|
959 | self.ui.write("%s\n" % p) | |||
|
960 | ||||
|
961 | def appliedname(self, index): | |||
|
962 | p = self.applied[index] | |||
|
963 | if not self.ui.verbose: | |||
|
964 | p = p.split(':')[1] | |||
|
965 | return p | |||
|
966 | ||||
|
967 | def top(self, repo): | |||
|
968 | if len(self.applied): | |||
|
969 | p = self.appliedname(-1) | |||
|
970 | self.ui.write(p + '\n') | |||
|
971 | else: | |||
|
972 | self.ui.write("No patches applied\n") | |||
|
973 | ||||
|
974 | def next(self, repo): | |||
|
975 | end = self.series_end() | |||
|
976 | if end == len(self.series): | |||
|
977 | self.ui.write("All patches applied\n") | |||
|
978 | else: | |||
|
979 | self.ui.write(self.series[end] + '\n') | |||
|
980 | ||||
|
981 | def prev(self, repo): | |||
|
982 | if len(self.applied) > 1: | |||
|
983 | p = self.appliedname(-2) | |||
|
984 | self.ui.write(p + '\n') | |||
|
985 | elif len(self.applied) == 1: | |||
|
986 | self.ui.write("Only one patch applied\n") | |||
|
987 | else: | |||
|
988 | self.ui.write("No patches applied\n") | |||
|
989 | ||||
|
990 | def qimport(self, repo, files, patch=None, existing=None, force=None): | |||
|
991 | if len(files) > 1 and patch: | |||
|
992 | self.ui.warn("-n option not valid when importing multiple files\n") | |||
|
993 | sys.exit(1) | |||
|
994 | i = 0 | |||
|
995 | for filename in files: | |||
|
996 | if existing: | |||
|
997 | if not patch: | |||
|
998 | patch = filename | |||
|
999 | if not os.path.isfile(os.path.join(self.path, patch)): | |||
|
1000 | self.ui.warn("patch %s does not exist\n" % patch) | |||
|
1001 | sys.exit(1) | |||
|
1002 | else: | |||
|
1003 | try: | |||
|
1004 | text = file(filename).read() | |||
|
1005 | except IOError: | |||
|
1006 | self.ui.warn("Unable to read %s\n" % patch) | |||
|
1007 | sys.exit(1) | |||
|
1008 | if not patch: | |||
|
1009 | patch = os.path.split(filename)[1] | |||
|
1010 | if not force and os.path.isfile(os.path.join(self.path, patch)): | |||
|
1011 | self.ui.warn("patch %s already exists\n" % patch) | |||
|
1012 | sys.exit(1) | |||
|
1013 | patchf = self.opener(patch, "w") | |||
|
1014 | patchf.write(text) | |||
|
1015 | if patch in self.series: | |||
|
1016 | self.ui.warn("patch %s is already in the series file\n" % patch) | |||
|
1017 | sys.exit(1) | |||
|
1018 | index = self.series_end() + i | |||
|
1019 | self.full_series[index:index] = [patch] | |||
|
1020 | self.read_series(self.full_series) | |||
|
1021 | self.ui.warn("adding %s to series file\n" % patch) | |||
|
1022 | i += 1 | |||
|
1023 | patch = None | |||
|
1024 | self.series_dirty = 1 | |||
|
1025 | ||||
|
1026 | def delete(ui, repo, patch, **opts): | |||
|
1027 | """remove a patch from the series file""" | |||
|
1028 | q = repomap[repo] | |||
|
1029 | q.delete(repo, patch) | |||
|
1030 | q.save_dirty() | |||
|
1031 | return 0 | |||
|
1032 | ||||
|
1033 | def applied(ui, repo, patch=None, **opts): | |||
|
1034 | """print the patches already applied""" | |||
|
1035 | repomap[repo].qapplied(repo, patch) | |||
|
1036 | return 0 | |||
|
1037 | ||||
|
1038 | def unapplied(ui, repo, patch=None, **opts): | |||
|
1039 | """print the patches not yet applied""" | |||
|
1040 | repomap[repo].unapplied(repo, patch) | |||
|
1041 | return 0 | |||
|
1042 | ||||
|
1043 | def qimport(ui, repo, *filename, **opts): | |||
|
1044 | """import a patch""" | |||
|
1045 | q = repomap[repo] | |||
|
1046 | q.qimport(repo, filename, patch=opts['name'], | |||
|
1047 | existing=opts['existing'], force=opts['force']) | |||
|
1048 | q.save_dirty() | |||
|
1049 | return 0 | |||
|
1050 | ||||
|
1051 | def init(ui, repo, **opts): | |||
|
1052 | """init a new queue repository""" | |||
|
1053 | q = repomap[repo] | |||
|
1054 | r = q.init(repo, create=opts['create_repo']) | |||
|
1055 | q.save_dirty() | |||
|
1056 | if r: | |||
|
1057 | fp = r.wopener('.hgignore', 'w') | |||
|
1058 | print >> fp, 'syntax: glob' | |||
|
1059 | print >> fp, 'status' | |||
|
1060 | fp.close() | |||
|
1061 | r.wopener('series', 'w').close() | |||
|
1062 | r.add(['.hgignore', 'series']) | |||
|
1063 | return 0 | |||
|
1064 | ||||
|
1065 | def commit(ui, repo, *pats, **opts): | |||
|
1066 | q = repomap[repo] | |||
|
1067 | r = q.qrepo() | |||
|
1068 | if not r: raise util.Abort('no queue repository') | |||
|
1069 | commands.commit(r.ui, r, *pats, **opts) | |||
|
1070 | ||||
|
1071 | def series(ui, repo, **opts): | |||
|
1072 | """print the entire series file""" | |||
|
1073 | repomap[repo].qseries(repo, missing=opts['missing']) | |||
|
1074 | return 0 | |||
|
1075 | ||||
|
1076 | def top(ui, repo, **opts): | |||
|
1077 | """print the name of the current patch""" | |||
|
1078 | repomap[repo].top(repo) | |||
|
1079 | return 0 | |||
|
1080 | ||||
|
1081 | def next(ui, repo, **opts): | |||
|
1082 | """print the name of the next patch""" | |||
|
1083 | repomap[repo].next(repo) | |||
|
1084 | return 0 | |||
|
1085 | ||||
|
1086 | def prev(ui, repo, **opts): | |||
|
1087 | """print the name of the previous patch""" | |||
|
1088 | repomap[repo].prev(repo) | |||
|
1089 | return 0 | |||
|
1090 | ||||
|
1091 | def new(ui, repo, patch, **opts): | |||
|
1092 | """create a new patch""" | |||
|
1093 | q = repomap[repo] | |||
|
1094 | q.new(repo, patch, msg=opts['message'], force=opts['force']) | |||
|
1095 | q.save_dirty() | |||
|
1096 | return 0 | |||
|
1097 | ||||
|
1098 | def refresh(ui, repo, **opts): | |||
|
1099 | """update the current patch""" | |||
|
1100 | q = repomap[repo] | |||
|
1101 | q.refresh(repo, short=opts['short']) | |||
|
1102 | q.save_dirty() | |||
|
1103 | return 0 | |||
|
1104 | ||||
|
1105 | def diff(ui, repo, *files, **opts): | |||
|
1106 | """diff of the current patch""" | |||
|
1107 | repomap[repo].diff(repo, files) | |||
|
1108 | return 0 | |||
|
1109 | ||||
|
1110 | def lastsavename(path): | |||
|
1111 | (dir, base) = os.path.split(path) | |||
|
1112 | names = os.listdir(dir) | |||
|
1113 | namere = re.compile("%s.([0-9]+)" % base) | |||
|
1114 | max = None | |||
|
1115 | maxname = None | |||
|
1116 | for f in names: | |||
|
1117 | m = namere.match(f) | |||
|
1118 | if m: | |||
|
1119 | index = int(m.group(1)) | |||
|
1120 | if max == None or index > max: | |||
|
1121 | max = index | |||
|
1122 | maxname = f | |||
|
1123 | if maxname: | |||
|
1124 | return (os.path.join(dir, maxname), max) | |||
|
1125 | return (None, None) | |||
|
1126 | ||||
|
1127 | def savename(path): | |||
|
1128 | (last, index) = lastsavename(path) | |||
|
1129 | if last is None: | |||
|
1130 | index = 0 | |||
|
1131 | newpath = path + ".%d" % (index + 1) | |||
|
1132 | return newpath | |||
|
1133 | ||||
|
1134 | def push(ui, repo, patch=None, **opts): | |||
|
1135 | """push the next patch onto the stack""" | |||
|
1136 | q = repomap[repo] | |||
|
1137 | mergeq = None | |||
|
1138 | ||||
|
1139 | if opts['all']: | |||
|
1140 | patch = q.series[-1] | |||
|
1141 | if opts['merge']: | |||
|
1142 | if opts['name']: | |||
|
1143 | newpath = opts['name'] | |||
|
1144 | else: | |||
|
1145 | newpath, i = lastsavename(q.path) | |||
|
1146 | if not newpath: | |||
|
1147 | ui.warn("no saved queues found, please use -n\n") | |||
|
1148 | return 1 | |||
|
1149 | mergeq = queue(ui, repo.join(""), newpath) | |||
|
1150 | ui.warn("merging with queue at: %s\n" % mergeq.path) | |||
|
1151 | ret = q.push(repo, patch, force=opts['force'], list=opts['list'], | |||
|
1152 | mergeq=mergeq) | |||
|
1153 | q.save_dirty() | |||
|
1154 | return ret | |||
|
1155 | ||||
|
1156 | def pop(ui, repo, patch=None, **opts): | |||
|
1157 | """pop the current patch off the stack""" | |||
|
1158 | localupdate = True | |||
|
1159 | if opts['name']: | |||
|
1160 | q = queue(ui, repo.join(""), repo.join(opts['name'])) | |||
|
1161 | ui.warn('using patch queue: %s\n' % q.path) | |||
|
1162 | localupdate = False | |||
|
1163 | else: | |||
|
1164 | q = repomap[repo] | |||
|
1165 | if opts['all'] and len(q.applied) > 0: | |||
|
1166 | patch = q.applied[0].split(':')[1] | |||
|
1167 | q.pop(repo, patch, force=opts['force'], update=localupdate) | |||
|
1168 | q.save_dirty() | |||
|
1169 | return 0 | |||
|
1170 | ||||
|
1171 | def restore(ui, repo, rev, **opts): | |||
|
1172 | """restore the queue state saved by a rev""" | |||
|
1173 | rev = repo.lookup(rev) | |||
|
1174 | q = repomap[repo] | |||
|
1175 | q.restore(repo, rev, delete=opts['delete'], | |||
|
1176 | qupdate=opts['update']) | |||
|
1177 | q.save_dirty() | |||
|
1178 | return 0 | |||
|
1179 | ||||
|
1180 | def save(ui, repo, **opts): | |||
|
1181 | """save current queue state""" | |||
|
1182 | q = repomap[repo] | |||
|
1183 | ret = q.save(repo, msg=opts['message']) | |||
|
1184 | if ret: | |||
|
1185 | return ret | |||
|
1186 | q.save_dirty() | |||
|
1187 | if opts['copy']: | |||
|
1188 | path = q.path | |||
|
1189 | if opts['name']: | |||
|
1190 | newpath = os.path.join(q.basepath, opts['name']) | |||
|
1191 | if os.path.exists(newpath): | |||
|
1192 | if not os.path.isdir(newpath): | |||
|
1193 | ui.warn("destination %s exists and is not a directory\n" % | |||
|
1194 | newpath) | |||
|
1195 | sys.exit(1) | |||
|
1196 | if not opts['force']: | |||
|
1197 | ui.warn("destination %s exists, use -f to force\n" % | |||
|
1198 | newpath) | |||
|
1199 | sys.exit(1) | |||
|
1200 | else: | |||
|
1201 | newpath = savename(path) | |||
|
1202 | ui.warn("copy %s to %s\n" % (path, newpath)) | |||
|
1203 | util.copyfiles(path, newpath) | |||
|
1204 | if opts['empty']: | |||
|
1205 | try: | |||
|
1206 | os.unlink(os.path.join(q.path, q.status_path)) | |||
|
1207 | except: | |||
|
1208 | pass | |||
|
1209 | return 0 | |||
|
1210 | ||||
|
1211 | def strip(ui, repo, rev, **opts): | |||
|
1212 | """strip a revision and all later revs on the same branch""" | |||
|
1213 | rev = repo.lookup(rev) | |||
|
1214 | backup = 'all' | |||
|
1215 | if opts['backup']: | |||
|
1216 | backup = 'strip' | |||
|
1217 | elif opts['nobackup']: | |||
|
1218 | backup = 'none' | |||
|
1219 | repomap[repo].strip(repo, rev, backup=backup) | |||
|
1220 | return 0 | |||
|
1221 | ||||
|
1222 | def version(ui, q=None): | |||
|
1223 | """print the version number""" | |||
|
1224 | ui.write("mq version %s\n" % versionstr) | |||
|
1225 | return 0 | |||
|
1226 | ||||
|
1227 | def reposetup(ui, repo): | |||
|
1228 | repomap[repo] = queue(ui, repo.join("")) | |||
|
1229 | ||||
|
1230 | cmdtable = { | |||
|
1231 | "qapplied": (applied, [], 'hg qapplied [patch]'), | |||
|
1232 | "qcommit|qci": | |||
|
1233 | (commit, | |||
|
1234 | [('A', 'addremove', None, _('run addremove during commit')), | |||
|
1235 | ('I', 'include', [], _('include names matching the given patterns')), | |||
|
1236 | ('X', 'exclude', [], _('exclude names matching the given patterns')), | |||
|
1237 | ('m', 'message', '', _('use <text> as commit message')), | |||
|
1238 | ('l', 'logfile', '', _('read the commit message from <file>')), | |||
|
1239 | ('d', 'date', '', _('record datecode as commit date')), | |||
|
1240 | ('u', 'user', '', _('record user as commiter'))], | |||
|
1241 | 'hg qcommit [options] [files]'), | |||
|
1242 | "^qdiff": (diff, [], 'hg qdiff [files]'), | |||
|
1243 | "qdelete": (delete, [], 'hg qdelete [patch]'), | |||
|
1244 | "^qimport": | |||
|
1245 | (qimport, | |||
|
1246 | [('e', 'existing', None, 'import file in patch dir'), | |||
|
1247 | ('n', 'name', '', 'patch file name'), | |||
|
1248 | ('f', 'force', None, 'overwrite existing files')], | |||
|
1249 | 'hg qimport'), | |||
|
1250 | "^qinit": | |||
|
1251 | (init, | |||
|
1252 | [('c', 'create-repo', None, 'create patch repository')], | |||
|
1253 | 'hg [-c] qinit'), | |||
|
1254 | "qnew": | |||
|
1255 | (new, | |||
|
1256 | [('m', 'message', '', 'commit message'), | |||
|
1257 | ('f', 'force', None, 'force')], | |||
|
1258 | 'hg qnew [-m message ] patch'), | |||
|
1259 | "qnext": (next, [], 'hg qnext'), | |||
|
1260 | "qprev": (prev, [], 'hg qprev'), | |||
|
1261 | "^qpop": | |||
|
1262 | (pop, | |||
|
1263 | [('a', 'all', None, 'pop all patches'), | |||
|
1264 | ('n', 'name', '', 'queue name to pop'), | |||
|
1265 | ('f', 'force', None, 'forget any local changes')], | |||
|
1266 | 'hg qpop [options] [patch/index]'), | |||
|
1267 | "^qpush": | |||
|
1268 | (push, | |||
|
1269 | [('f', 'force', None, 'apply if the patch has rejects'), | |||
|
1270 | ('l', 'list', None, 'list patch name in commit text'), | |||
|
1271 | ('a', 'all', None, 'apply all patches'), | |||
|
1272 | ('m', 'merge', None, 'merge from another queue'), | |||
|
1273 | ('n', 'name', '', 'merge queue name')], | |||
|
1274 | 'hg qpush [options] [patch/index]'), | |||
|
1275 | "^qrefresh": | |||
|
1276 | (refresh, | |||
|
1277 | [('s', 'short', None, 'short refresh')], | |||
|
1278 | 'hg qrefresh'), | |||
|
1279 | "qrestore": | |||
|
1280 | (restore, | |||
|
1281 | [('d', 'delete', None, 'delete save entry'), | |||
|
1282 | ('u', 'update', None, 'update queue working dir')], | |||
|
1283 | 'hg qrestore rev'), | |||
|
1284 | "qsave": | |||
|
1285 | (save, | |||
|
1286 | [('m', 'message', '', 'commit message'), | |||
|
1287 | ('c', 'copy', None, 'copy patch directory'), | |||
|
1288 | ('n', 'name', '', 'copy directory name'), | |||
|
1289 | ('e', 'empty', None, 'clear queue status file'), | |||
|
1290 | ('f', 'force', None, 'force copy')], | |||
|
1291 | 'hg qsave'), | |||
|
1292 | "qseries": | |||
|
1293 | (series, | |||
|
1294 | [('m', 'missing', None, 'print patches not in series')], | |||
|
1295 | 'hg qseries'), | |||
|
1296 | "^strip": | |||
|
1297 | (strip, | |||
|
1298 | [('f', 'force', None, 'force multi-head removal'), | |||
|
1299 | ('b', 'backup', None, 'bundle unrelated changesets'), | |||
|
1300 | ('n', 'nobackup', None, 'no backups')], | |||
|
1301 | 'hg strip rev'), | |||
|
1302 | "qtop": (top, [], 'hg qtop'), | |||
|
1303 | "qunapplied": (unapplied, [], 'hg qunapplied [patch]'), | |||
|
1304 | "qversion": (version, [], 'hg qversion') | |||
|
1305 | } | |||
|
1306 |
@@ -0,0 +1,13 b'' | |||||
|
1 | #!/bin/sh | |||
|
2 | hg init 1 | |||
|
3 | echo '[ui]' >> 1/.hg/hgrc | |||
|
4 | echo 'timeout = 10' >> 1/.hg/hgrc | |||
|
5 | echo foo > 1/foo | |||
|
6 | hg --cwd 1 ci -A -m foo | |||
|
7 | hg clone 1 2 | |||
|
8 | hg clone 2 3 | |||
|
9 | echo '[hooks]' >> 2/.hg/hgrc | |||
|
10 | echo 'changegroup.push = hg push -qf ../1' >> 2/.hg/hgrc | |||
|
11 | echo bar >> 3/foo | |||
|
12 | hg --cwd 3 ci -m bar | |||
|
13 | hg --cwd 3 push ../2 |
@@ -0,0 +1,7 b'' | |||||
|
1 | adding foo | |||
|
2 | pushing to ../2 | |||
|
3 | searching for changes | |||
|
4 | adding changesets | |||
|
5 | adding manifests | |||
|
6 | adding file changes | |||
|
7 | added 1 changesets with 1 changes to 1 files |
@@ -12,6 +12,7 b' tests/*.err' | |||||
12 | build |
|
12 | build | |
13 | dist |
|
13 | dist | |
14 | doc/*.[0-9] |
|
14 | doc/*.[0-9] | |
|
15 | doc/*.[0-9].gendoc.txt | |||
15 | doc/*.[0-9].{x,ht}ml |
|
16 | doc/*.[0-9].{x,ht}ml | |
16 | MANIFEST |
|
17 | MANIFEST | |
17 | patches |
|
18 | patches |
@@ -1,27 +1,5 b'' | |||||
1 | shopt -s extglob |
|
1 | shopt -s extglob | |
2 |
|
2 | |||
3 | _hg_command_list() |
|
|||
4 | { |
|
|||
5 | "$hg" --debug help 2>/dev/null | \ |
|
|||
6 | awk 'function command_line(line) { |
|
|||
7 | gsub(/,/, "", line) |
|
|||
8 | gsub(/:.*/, "", line) |
|
|||
9 | split(line, aliases) |
|
|||
10 | command = aliases[1] |
|
|||
11 | delete aliases[1] |
|
|||
12 | print command |
|
|||
13 | for (i in aliases) |
|
|||
14 | if (index(command, aliases[i]) != 1) |
|
|||
15 | print aliases[i] |
|
|||
16 | } |
|
|||
17 | /^list of commands:/ {commands=1} |
|
|||
18 | commands && /^ debug/ {a[i++] = $0; next;} |
|
|||
19 | commands && /^ [^ ]/ {command_line($0)} |
|
|||
20 | /^global options:/ {exit 0} |
|
|||
21 | END {for (i in a) command_line(a[i])}' |
|
|||
22 |
|
||||
23 | } |
|
|||
24 |
|
||||
25 | _hg_option_list() |
|
3 | _hg_option_list() | |
26 | { |
|
4 | { | |
27 | "$hg" -v help $1 2>/dev/null | \ |
|
5 | "$hg" -v help $1 2>/dev/null | \ | |
@@ -37,21 +15,9 b' shopt -s extglob' | |||||
37 |
|
15 | |||
38 | _hg_commands() |
|
16 | _hg_commands() | |
39 | { |
|
17 | { | |
40 |
local |
|
18 | local commands | |
41 |
|
19 | commands="$("$hg" debugcomplete "$cur" 2>/dev/null)" || commands="" | ||
42 | all=$(_hg_command_list) |
|
20 | COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur")) | |
43 | commands=${all%%$'\n'debug*} |
|
|||
44 | result=$(compgen -W '$commands' -- "$cur") |
|
|||
45 |
|
||||
46 | # hide debug commands from users, but complete them if |
|
|||
47 | # there is no other possible command |
|
|||
48 | if [ "$result" = "" ]; then |
|
|||
49 | local debug |
|
|||
50 | debug=debug${all#*$'\n'debug} |
|
|||
51 | result=$(compgen -W '$debug' -- "$cur") |
|
|||
52 | fi |
|
|||
53 |
|
||||
54 | COMPREPLY=(${COMPREPLY[@]:-} $result) |
|
|||
55 | } |
|
21 | } | |
56 |
|
22 | |||
57 | _hg_paths() |
|
23 | _hg_paths() |
@@ -1,7 +1,7 b'' | |||||
1 | Summary: Mercurial -- a distributed SCM |
|
1 | Summary: Mercurial -- a distributed SCM | |
2 | Name: mercurial |
|
2 | Name: mercurial | |
3 |
Version: 0. |
|
3 | Version: 0.8 | |
4 |
Release: |
|
4 | Release: 0 | |
5 | License: GPL |
|
5 | License: GPL | |
6 | Group: Development/Tools |
|
6 | Group: Development/Tools | |
7 | Source: http://www.selenic.com/mercurial/release/%{name}-%{version}.tar.gz |
|
7 | Source: http://www.selenic.com/mercurial/release/%{name}-%{version}.tar.gz | |
@@ -10,6 +10,7 b' BuildRoot: /tmp/build.%{name}-%{version}' | |||||
10 |
|
10 | |||
11 | %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))') |
|
11 | %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))') | |
12 | %define pythonlib %{_libdir}/python%{pythonver}/site-packages/%{name} |
|
12 | %define pythonlib %{_libdir}/python%{pythonver}/site-packages/%{name} | |
|
13 | %define hgext %{_libdir}/python%{pythonver}/site-packages/hgext | |||
13 |
|
14 | |||
14 | %description |
|
15 | %description | |
15 | Mercurial is a fast, lightweight source control management system designed |
|
16 | Mercurial is a fast, lightweight source control management system designed | |
@@ -30,10 +31,12 b' rm -rf $RPM_BUILD_ROOT' | |||||
30 |
|
31 | |||
31 | %files |
|
32 | %files | |
32 | %defattr(-,root,root,-) |
|
33 | %defattr(-,root,root,-) | |
33 |
%doc doc/* |
|
34 | %doc doc/* *.cgi | |
34 | %dir %{pythonlib} |
|
35 | %dir %{pythonlib} | |
|
36 | %dir %{hgext} | |||
35 | %{_bindir}/hgmerge |
|
37 | %{_bindir}/hgmerge | |
36 | %{_bindir}/hg |
|
38 | %{_bindir}/hg | |
37 | %{pythonlib}/templates |
|
39 | %{pythonlib}/templates | |
38 | %{pythonlib}/*.py* |
|
40 | %{pythonlib}/*.py* | |
39 | %{pythonlib}/*.so |
|
41 | %{pythonlib}/*.so | |
|
42 | %{hgext}/*.py* |
@@ -28,23 +28,22 b' AllowNoIcons=true' | |||||
28 | DefaultGroupName=Mercurial |
|
28 | DefaultGroupName=Mercurial | |
29 |
|
29 | |||
30 | [Files] |
|
30 | [Files] | |
31 | Source: templates\*.*; DestDir: {app}\Templates; Flags: recursesubdirs createallsubdirs |
|
31 | Source: ..\..\msys\1.0\bin\patch.exe; DestDir: {app} | |
32 | Source: contrib\mercurial.el; DestDir: {app}/Contrib |
|
32 | Source: contrib\mercurial.el; DestDir: {app}/Contrib | |
33 |
Source: contrib\ |
|
33 | Source: contrib\win32\ReadMe.html; DestDir: {app}; Flags: isreadme | |
34 | Source: dist\w9xpopen.exe; DestDir: {app} |
|
34 | Source: contrib\win32\mercurial.ini; DestDir: {app}; DestName: Mercurial.ini; Flags: confirmoverwrite | |
|
35 | Source: contrib\win32\postinstall.txt; DestDir: {app}; DestName: ReleaseNotes.txt | |||
35 | Source: dist\hg.exe; DestDir: {app} |
|
36 | Source: dist\hg.exe; DestDir: {app} | |
|
37 | Source: dist\library.zip; DestDir: {app} | |||
|
38 | Source: dist\mfc71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt | |||
36 | Source: dist\msvcr71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt |
|
39 | Source: dist\msvcr71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt | |
37 |
Source: dist\ |
|
40 | Source: dist\w9xpopen.exe; DestDir: {app} | |
38 | Source: doc\*.txt; DestDir: {app}\Docs |
|
41 | Source: doc\*.txt; DestDir: {app}\Docs | |
39 | Source: dist\mfc71.dll; DestDir: {sys}; Flags: sharedfile uninsnosharedfileprompt |
|
42 | Source: templates\*.*; DestDir: {app}\Templates; Flags: recursesubdirs createallsubdirs | |
|
43 | Source: CONTRIBUTORS; DestDir: {app}; DestName: Contributors.txt | |||
40 | Source: COPYING; DestDir: {app}; DestName: Copying.txt |
|
44 | Source: COPYING; DestDir: {app}; DestName: Copying.txt | |
41 | Source: comparison.txt; DestDir: {app}\Docs; DestName: Comparison.txt |
|
45 | Source: comparison.txt; DestDir: {app}\Docs; DestName: Comparison.txt | |
42 | Source: notes.txt; DestDir: {app}\Docs; DestName: DesignNotes.txt |
|
46 | Source: notes.txt; DestDir: {app}\Docs; DestName: DesignNotes.txt | |
43 | Source: CONTRIBUTORS; DestDir: {app}; DestName: Contributors.txt |
|
|||
44 | Source: contrib\win32\ReadMe.html; DestDir: {app}; Flags: isreadme |
|
|||
45 | Source: ..\..\msys\1.0\bin\patch.exe; DestDir: {app} |
|
|||
46 | Source: contrib\win32\mercurial.ini; DestDir: {app}; DestName: Mercurial.ini; Flags: confirmoverwrite |
|
|||
47 | Source: contrib\win32\postinstall.txt; DestDir: {app}; DestName: ReleaseNotes.txt |
|
|||
48 |
|
47 | |||
49 | [INI] |
|
48 | [INI] | |
50 | Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: http://www.selenic.com/mercurial/ |
|
49 | Filename: {app}\Mercurial.url; Section: InternetShortcut; Key: URL; String: http://www.selenic.com/mercurial/ |
@@ -8,6 +8,12 b' man: $(MAN)' | |||||
8 |
|
8 | |||
9 | html: $(HTML) |
|
9 | html: $(HTML) | |
10 |
|
10 | |||
|
11 | hg.1.txt: hg.1.gendoc.txt | |||
|
12 | touch hg.1.txt | |||
|
13 | ||||
|
14 | hg.1.gendoc.txt: ../mercurial/commands.py | |||
|
15 | python gendoc.py > $@ | |||
|
16 | ||||
11 | %: %.xml |
|
17 | %: %.xml | |
12 | xmlto man $*.xml |
|
18 | xmlto man $*.xml | |
13 |
|
19 | |||
@@ -18,4 +24,4 b' html: $(HTML)' | |||||
18 | asciidoc -b html4 $*.txt || asciidoc -b html $*.txt |
|
24 | asciidoc -b html4 $*.txt || asciidoc -b html $*.txt | |
19 |
|
25 | |||
20 | clean: |
|
26 | clean: | |
21 | $(RM) $(MAN) $(MAN:%=%.xml) $(MAN:%=%.html) |
|
27 | $(RM) $(MAN) $(MAN:%=%.xml) $(MAN:%=%.html) *.[0-9].gendoc.txt |
This diff has been collapsed as it changes many lines, (647 lines changed) Show them Hide them | |||||
@@ -14,42 +14,6 b' DESCRIPTION' | |||||
14 | ----------- |
|
14 | ----------- | |
15 | The hg(1) command provides a command line interface to the Mercurial system. |
|
15 | The hg(1) command provides a command line interface to the Mercurial system. | |
16 |
|
16 | |||
17 | OPTIONS |
|
|||
18 | ------- |
|
|||
19 |
|
||||
20 | -R, --repository:: |
|
|||
21 | repository root directory |
|
|||
22 |
|
||||
23 | --cwd:: |
|
|||
24 | change working directory |
|
|||
25 |
|
||||
26 | -y, --noninteractive:: |
|
|||
27 | do not prompt, assume 'yes' for any required answers |
|
|||
28 |
|
||||
29 | -q, --quiet:: |
|
|||
30 | suppress output |
|
|||
31 |
|
||||
32 | -v, --verbose:: |
|
|||
33 | enable additional output |
|
|||
34 |
|
||||
35 | --debug:: |
|
|||
36 | enable debugging output |
|
|||
37 |
|
||||
38 | --traceback:: |
|
|||
39 | print traceback on exception |
|
|||
40 |
|
||||
41 | --time:: |
|
|||
42 | time how long the command takes |
|
|||
43 |
|
||||
44 | --profile:: |
|
|||
45 | print command execution profile |
|
|||
46 |
|
||||
47 | --version:: |
|
|||
48 | output version information and exit |
|
|||
49 |
|
||||
50 | -h, --help:: |
|
|||
51 | display help and exit |
|
|||
52 |
|
||||
53 | COMMAND ELEMENTS |
|
17 | COMMAND ELEMENTS | |
54 | ---------------- |
|
18 | ---------------- | |
55 |
|
19 | |||
@@ -70,617 +34,8 b' repository path::' | |||||
70 | fast and the old-http:// protocol which is much slower but does not |
|
34 | fast and the old-http:// protocol which is much slower but does not | |
71 | require a special server on the web host. |
|
35 | require a special server on the web host. | |
72 |
|
36 | |||
73 | COMMANDS |
|
|||
74 | -------- |
|
|||
75 |
|
37 | |||
76 | add [options] [files ...]:: |
|
38 | include::hg.1.gendoc.txt[] | |
77 | Schedule files to be version controlled and added to the repository. |
|
|||
78 |
|
||||
79 | The files will be added to the repository at the next commit. |
|
|||
80 |
|
||||
81 | If no names are given, add all files in the current directory and |
|
|||
82 | its subdirectories. |
|
|||
83 |
|
||||
84 | addremove [options] [files ...]:: |
|
|||
85 | Add all new files and remove all missing files from the repository. |
|
|||
86 |
|
||||
87 | New files are ignored if they match any of the patterns in .hgignore. As |
|
|||
88 | with add, these changes take effect at the next commit. |
|
|||
89 |
|
||||
90 | annotate [-r <rev> -u -n -c -d] [files ...]:: |
|
|||
91 | List changes in files, showing the revision id responsible for each line |
|
|||
92 |
|
||||
93 | This command is useful to discover who did a change or when a change took |
|
|||
94 | place. |
|
|||
95 |
|
||||
96 | Without the -a option, annotate will avoid processing files it |
|
|||
97 | detects as binary. With -a, annotate will generate an annotation |
|
|||
98 | anyway, probably with undesirable results. |
|
|||
99 |
|
||||
100 | options: |
|
|||
101 | -a, --text treat all files as text |
|
|||
102 | -I, --include <pat> include names matching the given patterns |
|
|||
103 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
104 | -r, --revision <rev> annotate the specified revision |
|
|||
105 | -u, --user list the author |
|
|||
106 | -d, --date list the commit date |
|
|||
107 | -c, --changeset list the changeset |
|
|||
108 | -n, --number list the revision number (default) |
|
|||
109 |
|
||||
110 | bundle <file> <other>:: |
|
|||
111 | (EXPERIMENTAL) |
|
|||
112 |
|
||||
113 | Generate a compressed changegroup file collecting all changesets |
|
|||
114 | not found in the other repository. |
|
|||
115 |
|
||||
116 | This file can then be transferred using conventional means and |
|
|||
117 | applied to another repository with the unbundle command. This is |
|
|||
118 | useful when native push and pull are not available or when |
|
|||
119 | exporting an entire repository is undesirable. The standard file |
|
|||
120 | extension is ".hg". |
|
|||
121 |
|
||||
122 | Unlike import/export, this exactly preserves all changeset |
|
|||
123 | contents including permissions, rename data, and revision history. |
|
|||
124 |
|
||||
125 | cat [options] <file ...>:: |
|
|||
126 | Print the specified files as they were at the given revision. |
|
|||
127 | If no revision is given then the tip is used. |
|
|||
128 |
|
||||
129 | Output may be to a file, in which case the name of the file is |
|
|||
130 | given using a format string. The formatting rules are the same as |
|
|||
131 | for the export command, with the following additions: |
|
|||
132 |
|
||||
133 | %s basename of file being printed |
|
|||
134 | %d dirname of file being printed, or '.' if in repo root |
|
|||
135 | %p root-relative path name of file being printed |
|
|||
136 |
|
||||
137 | options: |
|
|||
138 | -I, --include <pat> include names matching the given patterns |
|
|||
139 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
140 | -o, --output <filespec> print output to file with formatted name |
|
|||
141 | -r, --rev <rev> print the given revision |
|
|||
142 |
|
||||
143 | clone [options] <source> [dest]:: |
|
|||
144 | Create a copy of an existing repository in a new directory. |
|
|||
145 |
|
||||
146 | If no destination directory name is specified, it defaults to the |
|
|||
147 | basename of the source. |
|
|||
148 |
|
||||
149 | The location of the source is added to the new repository's |
|
|||
150 | .hg/hgrc file, as the default to be used for future pulls. |
|
|||
151 |
|
||||
152 | For efficiency, hardlinks are used for cloning whenever the source |
|
|||
153 | and destination are on the same filesystem. Some filesystems, |
|
|||
154 | such as AFS, implement hardlinking incorrectly, but do not report |
|
|||
155 | errors. In these cases, use the --pull option to avoid |
|
|||
156 | hardlinking. |
|
|||
157 |
|
||||
158 | See pull for valid source format details. |
|
|||
159 |
|
||||
160 | options: |
|
|||
161 | -U, --noupdate do not update the new working directory |
|
|||
162 | --pull use pull protocol to copy metadata |
|
|||
163 | -e, --ssh specify ssh command to use |
|
|||
164 | --remotecmd specify hg command to run on the remote side |
|
|||
165 |
|
||||
166 | commit [options] [files...]:: |
|
|||
167 | Commit changes to the given files into the repository. |
|
|||
168 |
|
||||
169 | If a list of files is omitted, all changes reported by "hg status" |
|
|||
170 | from the root of the repository will be commited. |
|
|||
171 |
|
||||
172 | The HGEDITOR or EDITOR environment variables are used to start an |
|
|||
173 | editor to add a commit comment. |
|
|||
174 |
|
||||
175 | Options: |
|
|||
176 |
|
||||
177 | -A, --addremove run addremove during commit |
|
|||
178 | -I, --include <pat> include names matching the given patterns |
|
|||
179 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
180 | -m, --message <text> use <text> as commit message |
|
|||
181 | -l, --logfile <file> read the commit message from <file> |
|
|||
182 | -d, --date <datecode> record datecode as commit date |
|
|||
183 | -u, --user <user> record user as commiter |
|
|||
184 |
|
||||
185 | aliases: ci |
|
|||
186 |
|
||||
187 | copy <source ...> <dest>:: |
|
|||
188 | Mark dest as having copies of source files. If dest is a |
|
|||
189 | directory, copies are put in that directory. If dest is a file, |
|
|||
190 | there can only be one source. |
|
|||
191 |
|
||||
192 | By default, this command copies the contents of files as they |
|
|||
193 | stand in the working directory. If invoked with --after, the |
|
|||
194 | operation is recorded, but no copying is performed. |
|
|||
195 |
|
||||
196 | This command takes effect in the next commit. |
|
|||
197 |
|
||||
198 | NOTE: This command should be treated as experimental. While it |
|
|||
199 | should properly record copied files, this information is not yet |
|
|||
200 | fully used by merge, nor fully reported by log. |
|
|||
201 |
|
||||
202 | Options: |
|
|||
203 | -A, --after record a copy that has already occurred |
|
|||
204 | -I, --include <pat> include names matching the given patterns |
|
|||
205 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
206 | -f, --force forcibly copy over an existing managed file |
|
|||
207 |
|
||||
208 | aliases: cp |
|
|||
209 |
|
||||
210 | diff [-a] [-r revision] [-r revision] [files ...]:: |
|
|||
211 | Show differences between revisions for the specified files. |
|
|||
212 |
|
||||
213 | Differences between files are shown using the unified diff format. |
|
|||
214 |
|
||||
215 | When two revision arguments are given, then changes are shown |
|
|||
216 | between those revisions. If only one revision is specified then |
|
|||
217 | that revision is compared to the working directory, and, when no |
|
|||
218 | revisions are specified, the working directory files are compared |
|
|||
219 | to its parent. |
|
|||
220 |
|
||||
221 | Without the -a option, diff will avoid generating diffs of files |
|
|||
222 | it detects as binary. With -a, diff will generate a diff anyway, |
|
|||
223 | probably with undesirable results. |
|
|||
224 |
|
||||
225 | options: |
|
|||
226 | -a, --text treat all files as text |
|
|||
227 | -I, --include <pat> include names matching the given patterns |
|
|||
228 | -p, --show-function show which function each change is in |
|
|||
229 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
230 | -w, --ignore-all-space ignore white space when comparing lines |
|
|||
231 |
|
||||
232 | export [-o filespec] [revision] ...:: |
|
|||
233 | Print the changeset header and diffs for one or more revisions. |
|
|||
234 |
|
||||
235 | The information shown in the changeset header is: author, |
|
|||
236 | changeset hash, parent and commit comment. |
|
|||
237 |
|
||||
238 | Output may be to a file, in which case the name of the file is |
|
|||
239 | given using a format string. The formatting rules are as follows: |
|
|||
240 |
|
||||
241 | %% literal "%" character |
|
|||
242 | %H changeset hash (40 bytes of hexadecimal) |
|
|||
243 | %N number of patches being generated |
|
|||
244 | %R changeset revision number |
|
|||
245 | %b basename of the exporting repository |
|
|||
246 | %h short-form changeset hash (12 bytes of hexadecimal) |
|
|||
247 | %n zero-padded sequence number, starting at 1 |
|
|||
248 | %r zero-padded changeset revision number |
|
|||
249 |
|
||||
250 | Without the -a option, export will avoid generating diffs of files |
|
|||
251 | it detects as binary. With -a, export will generate a diff anyway, |
|
|||
252 | probably with undesirable results. |
|
|||
253 |
|
||||
254 | options: |
|
|||
255 | -a, --text treat all files as text |
|
|||
256 | -o, --output <filespec> print output to file with formatted name |
|
|||
257 |
|
||||
258 | forget [options] [files]:: |
|
|||
259 | Undo an 'hg add' scheduled for the next commit. |
|
|||
260 |
|
||||
261 | options: |
|
|||
262 | -I, --include <pat> include names matching the given patterns |
|
|||
263 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
264 |
|
||||
265 | grep [options] pattern [files]:: |
|
|||
266 | Search revisions of files for a regular expression. |
|
|||
267 |
|
||||
268 | This command behaves differently than Unix grep. It only accepts |
|
|||
269 | Python/Perl regexps. It searches repository history, not the |
|
|||
270 | working directory. It always prints the revision number in which |
|
|||
271 | a match appears. |
|
|||
272 |
|
||||
273 | By default, grep only prints output for the first revision of a |
|
|||
274 | file in which it finds a match. To get it to print every revision |
|
|||
275 | that contains a change in match status ("-" for a match that |
|
|||
276 | becomes a non-match, or "+" for a non-match that becomes a match), |
|
|||
277 | use the --all flag. |
|
|||
278 |
|
||||
279 | options: |
|
|||
280 | -0, --print0 end fields with NUL |
|
|||
281 | -I, --include <pat> include names matching the given patterns |
|
|||
282 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
283 | --all print all revisions that match |
|
|||
284 | -i, --ignore-case ignore case when matching |
|
|||
285 | -l, --files-with-matches print only filenames and revs that match |
|
|||
286 | -n, --line-number print matching line numbers |
|
|||
287 | -r <rev>, --rev <rev> search in given revision range |
|
|||
288 | -u, --user print user who committed change |
|
|||
289 |
|
||||
290 | heads:: |
|
|||
291 | Show all repository head changesets. |
|
|||
292 |
|
||||
293 | Repository "heads" are changesets that don't have children |
|
|||
294 | changesets. They are where development generally takes place and |
|
|||
295 | are the usual targets for update and merge operations. |
|
|||
296 |
|
||||
297 | options: |
|
|||
298 | -b, --branches show branches |
|
|||
299 | -r, --rev <rev> show only heads which are descendants of rev |
|
|||
300 | --style <style> display using style map file |
|
|||
301 | --template <tpl> display using template |
|
|||
302 |
|
||||
303 | identify:: |
|
|||
304 | Print a short summary of the current state of the repo. |
|
|||
305 |
|
||||
306 | This summary identifies the repository state using one or two parent |
|
|||
307 | hash identifiers, followed by a "+" if there are uncommitted changes |
|
|||
308 | in the working directory, followed by a list of tags for this revision. |
|
|||
309 |
|
||||
310 | aliases: id |
|
|||
311 |
|
||||
312 | import [-p <n> -b <base> -f] <patches>:: |
|
|||
313 | Import a list of patches and commit them individually. |
|
|||
314 |
|
||||
315 | If there are outstanding changes in the working directory, import |
|
|||
316 | will abort unless given the -f flag. |
|
|||
317 |
|
||||
318 | If a patch looks like a mail message (its first line starts with |
|
|||
319 | "From " or looks like an RFC822 header), it will not be applied |
|
|||
320 | unless the -f option is used. The importer neither parses nor |
|
|||
321 | discards mail headers, so use -f only to override the "mailness" |
|
|||
322 | safety check, not to import a real mail message. |
|
|||
323 |
|
||||
324 | options: |
|
|||
325 | -p, --strip <n> directory strip option for patch. This has the same |
|
|||
326 | meaning as the corresponding patch option |
|
|||
327 | -b <path> base directory to read patches from |
|
|||
328 | -f, --force skip check for outstanding uncommitted changes |
|
|||
329 |
|
||||
330 | aliases: patch |
|
|||
331 |
|
||||
332 | incoming [-p] [source]:: |
|
|||
333 | Show new changesets found in the specified repo or the default |
|
|||
334 | pull repo. These are the changesets that would be pulled if a pull |
|
|||
335 | was requested. |
|
|||
336 |
|
||||
337 | Currently only local repositories are supported. |
|
|||
338 |
|
||||
339 | options: |
|
|||
340 | -M, --no-merges do not show merges |
|
|||
341 | -n, --newest-first show newest records first |
|
|||
342 | -p, --patch show patch |
|
|||
343 | --style <style> display using style map file |
|
|||
344 | --template <tpl> display using template |
|
|||
345 |
|
||||
346 | aliases: in |
|
|||
347 |
|
||||
348 | init [dest]:: |
|
|||
349 | Initialize a new repository in the given directory. If the given |
|
|||
350 | directory does not exist, it is created. |
|
|||
351 |
|
||||
352 | If no directory is given, the current directory is used. |
|
|||
353 |
|
||||
354 | locate [options] [files]:: |
|
|||
355 | Print all files under Mercurial control whose names match the |
|
|||
356 | given patterns. |
|
|||
357 |
|
||||
358 | This command searches the current directory and its |
|
|||
359 | subdirectories. To search an entire repository, move to the root |
|
|||
360 | of the repository. |
|
|||
361 |
|
||||
362 | If no patterns are given to match, this command prints all file |
|
|||
363 | names. |
|
|||
364 |
|
||||
365 | If you want to feed the output of this command into the "xargs" |
|
|||
366 | command, use the "-0" option to both this command and "xargs". |
|
|||
367 | This will avoid the problem of "xargs" treating single filenames |
|
|||
368 | that contain white space as multiple filenames. |
|
|||
369 |
|
||||
370 | options: |
|
|||
371 |
|
||||
372 | -0, --print0 end filenames with NUL, for use with xargs |
|
|||
373 | -f, --fullpath print complete paths from the filesystem root |
|
|||
374 | -I, --include <pat> include names matching the given patterns |
|
|||
375 | -r, --rev <rev> search the repository as it stood at rev |
|
|||
376 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
377 |
|
||||
378 | log [-r revision ...] [-p] [files]:: |
|
|||
379 | Print the revision history of the specified files or the entire project. |
|
|||
380 |
|
||||
381 | By default this command outputs: changeset id and hash, tags, |
|
|||
382 | parents, user, date and time, and a summary for each commit. The |
|
|||
383 | -v switch adds some more detail, such as changed files, manifest |
|
|||
384 | hashes or message signatures. |
|
|||
385 |
|
||||
386 | options: |
|
|||
387 | -I, --include <pat> include names matching the given patterns |
|
|||
388 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
389 | -b, --branch show branches |
|
|||
390 | -k, --keyword <str> search for keywords |
|
|||
391 | -l, --limit <num> print no more than this many changes |
|
|||
392 | -M, --no-merges do not show merges |
|
|||
393 | -m, --only-merges only show merges |
|
|||
394 | -r, --rev <A> show the specified revision or range |
|
|||
395 | -p, --patch show patch |
|
|||
396 | --style <style> display using style map file |
|
|||
397 | --template <tpl> display using template |
|
|||
398 |
|
||||
399 | aliases: history |
|
|||
400 |
|
||||
401 | manifest [revision]:: |
|
|||
402 | Print a list of version controlled files for the given revision. |
|
|||
403 |
|
||||
404 | The manifest is the list of files being version controlled. If no revision |
|
|||
405 | is given then the tip is used. |
|
|||
406 |
|
||||
407 | outgoing [-p] [dest]:: |
|
|||
408 | Show changesets not found in the specified destination repo or the |
|
|||
409 | default push repo. These are the changesets that would be pushed |
|
|||
410 | if a push was requested. |
|
|||
411 |
|
||||
412 | See pull for valid source format details. |
|
|||
413 |
|
||||
414 | options: |
|
|||
415 | -M, --no-merges do not show merges |
|
|||
416 | -p, --patch show patch |
|
|||
417 | -n, --newest-first show newest records first |
|
|||
418 | --style <style> display using style map file |
|
|||
419 | --template <tpl> display using template |
|
|||
420 |
|
||||
421 | aliases: out |
|
|||
422 |
|
||||
423 | parents:: |
|
|||
424 | Print the working directory's parent revisions. |
|
|||
425 |
|
||||
426 | options: |
|
|||
427 | -b, --branches show branches |
|
|||
428 | --style <style> display using style map file |
|
|||
429 | --template <tpl> display using template |
|
|||
430 |
|
||||
431 | paths [NAME]:: |
|
|||
432 | Show definition of symbolic path name NAME. If no name is given, show |
|
|||
433 | definition of available names. |
|
|||
434 |
|
||||
435 | Path names are defined in the [paths] section of /etc/mercurial/hgrc |
|
|||
436 | and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too. |
|
|||
437 |
|
||||
438 | pull <repository path>:: |
|
|||
439 | Pull changes from a remote repository to a local one. |
|
|||
440 |
|
||||
441 | This finds all changes from the repository at the specified path |
|
|||
442 | or URL and adds them to the local repository. By default, this |
|
|||
443 | does not update the copy of the project in the working directory. |
|
|||
444 |
|
||||
445 | Valid URLs are of the form: |
|
|||
446 |
|
||||
447 | local/filesystem/path |
|
|||
448 | http://[user@]host[:port][/path] |
|
|||
449 | https://[user@]host[:port][/path] |
|
|||
450 | ssh://[user@]host[:port][/path] |
|
|||
451 |
|
||||
452 | SSH requires an accessible shell account on the destination machine |
|
|||
453 | and a copy of hg in the remote path. With SSH, paths are relative |
|
|||
454 | to the remote user's home directory by default; use two slashes at |
|
|||
455 | the start of a path to specify it as relative to the filesystem root. |
|
|||
456 |
|
||||
457 | options: |
|
|||
458 | -u, --update update the working directory to tip after pull |
|
|||
459 | -e, --ssh specify ssh command to use |
|
|||
460 | --remotecmd specify hg command to run on the remote side |
|
|||
461 |
|
||||
462 | push <destination>:: |
|
|||
463 | Push changes from the local repository to the given destination. |
|
|||
464 |
|
||||
465 | This is the symmetrical operation for pull. It helps to move |
|
|||
466 | changes from the current repository to a different one. If the |
|
|||
467 | destination is local this is identical to a pull in that directory |
|
|||
468 | from the current one. |
|
|||
469 |
|
||||
470 | By default, push will refuse to run if it detects the result would |
|
|||
471 | increase the number of remote heads. This generally indicates the |
|
|||
472 | the client has forgotten to sync and merge before pushing. |
|
|||
473 |
|
||||
474 | Valid URLs are of the form: |
|
|||
475 |
|
||||
476 | local/filesystem/path |
|
|||
477 | ssh://[user@]host[:port][/path] |
|
|||
478 |
|
||||
479 | SSH requires an accessible shell account on the destination |
|
|||
480 | machine and a copy of hg in the remote path. |
|
|||
481 |
|
||||
482 | options: |
|
|||
483 |
|
||||
484 | -f, --force force update |
|
|||
485 | -e, --ssh specify ssh command to use |
|
|||
486 | --remotecmd specify hg command to run on the remote side |
|
|||
487 |
|
||||
488 | rawcommit [-p -d -u -F -m -l]:: |
|
|||
489 | Lowlevel commit, for use in helper scripts. (DEPRECATED) |
|
|||
490 |
|
||||
491 | This command is not intended to be used by normal users, as it is |
|
|||
492 | primarily useful for importing from other SCMs. |
|
|||
493 |
|
||||
494 | This command is now deprecated and will be removed in a future |
|
|||
495 | release, please use debugsetparents and commit instead. |
|
|||
496 |
|
||||
497 | recover:: |
|
|||
498 | Recover from an interrupted commit or pull. |
|
|||
499 |
|
||||
500 | This command tries to fix the repository status after an interrupted |
|
|||
501 | operation. It should only be necessary when Mercurial suggests it. |
|
|||
502 |
|
||||
503 | remove [options] [files ...]:: |
|
|||
504 | Schedule the indicated files for removal from the repository. |
|
|||
505 |
|
||||
506 | This command schedules the files to be removed at the next commit. |
|
|||
507 | This only removes files from the current branch, not from the |
|
|||
508 | entire project history. If the files still exist in the working |
|
|||
509 | directory, they will be deleted from it. |
|
|||
510 |
|
||||
511 | aliases: rm |
|
|||
512 |
|
||||
513 | rename <source ...> <dest>:: |
|
|||
514 | Mark dest as copies of sources; mark sources for deletion. If |
|
|||
515 | dest is a directory, copies are put in that directory. If dest is |
|
|||
516 | a file, there can only be one source. |
|
|||
517 |
|
||||
518 | By default, this command copies the contents of files as they |
|
|||
519 | stand in the working directory. If invoked with --after, the |
|
|||
520 | operation is recorded, but no copying is performed. |
|
|||
521 |
|
||||
522 | This command takes effect in the next commit. |
|
|||
523 |
|
||||
524 | NOTE: This command should be treated as experimental. While it |
|
|||
525 | should properly record rename files, this information is not yet |
|
|||
526 | fully used by merge, nor fully reported by log. |
|
|||
527 |
|
||||
528 | Options: |
|
|||
529 | -A, --after record a rename that has already occurred |
|
|||
530 | -f, --force forcibly copy over an existing managed file |
|
|||
531 |
|
||||
532 | aliases: mv |
|
|||
533 |
|
||||
534 | revert [names ...]:: |
|
|||
535 | The revert command has two modes of operation. |
|
|||
536 |
|
||||
537 | In its default mode, it reverts any uncommitted modifications made |
|
|||
538 | to the named files or directories. This restores the contents of |
|
|||
539 | the affected files to an unmodified state. |
|
|||
540 |
|
||||
541 | Using the -r option, it reverts the given files or directories to |
|
|||
542 | their state as of an earlier revision. This can be helpful to "roll |
|
|||
543 | back" some or all of a change that should not have been committed. |
|
|||
544 |
|
||||
545 | Revert modifies the working directory. It does not commit any |
|
|||
546 | changes, or change the parent of the current working directory. |
|
|||
547 |
|
||||
548 | If a file has been deleted, it is recreated. If the executable |
|
|||
549 | mode of a file was changed, it is reset. |
|
|||
550 |
|
||||
551 | If a directory is given, all files in that directory and its |
|
|||
552 | subdirectories are reverted. |
|
|||
553 |
|
||||
554 | If no arguments are given, all files in the current directory and |
|
|||
555 | its subdirectories are reverted. |
|
|||
556 |
|
||||
557 | options: |
|
|||
558 | -r, --rev <rev> revision to revert to |
|
|||
559 | -n, --nonrecursive do not recurse into subdirectories |
|
|||
560 |
|
||||
561 | root:: |
|
|||
562 | Print the root directory of the current repository. |
|
|||
563 |
|
||||
564 | serve [options]:: |
|
|||
565 | Start a local HTTP repository browser and pull server. |
|
|||
566 |
|
||||
567 | By default, the server logs accesses to stdout and errors to |
|
|||
568 | stderr. Use the "-A" and "-E" options to log to files. |
|
|||
569 |
|
||||
570 | options: |
|
|||
571 | -A, --accesslog <file> name of access log file to write to |
|
|||
572 | -d, --daemon run server in background, as a daemon |
|
|||
573 | -E, --errorlog <file> name of error log file to write to |
|
|||
574 | -a, --address <addr> address to use |
|
|||
575 | -p, --port <n> port to use (default: 8000) |
|
|||
576 | -n, --name <name> name to show in web pages (default: working dir) |
|
|||
577 | --pid-file <file> write server process ID to given file |
|
|||
578 | -t, --templatedir <path> web templates to use |
|
|||
579 | -6, --ipv6 use IPv6 in addition to IPv4 |
|
|||
580 |
|
||||
581 | status [options] [files]:: |
|
|||
582 | Show changed files in the working directory. If no names are |
|
|||
583 | given, all files are shown. Otherwise, only files matching the |
|
|||
584 | given names are shown. |
|
|||
585 |
|
||||
586 | The codes used to show the status of files are: |
|
|||
587 |
|
||||
588 | M = changed |
|
|||
589 | A = added |
|
|||
590 | R = removed |
|
|||
591 | ? = not tracked |
|
|||
592 |
|
||||
593 | options: |
|
|||
594 |
|
||||
595 | -m, --modified show only modified files |
|
|||
596 | -a, --added show only added files |
|
|||
597 | -r, --removed show only removed files |
|
|||
598 | -u, --unknown show only unknown (not tracked) files |
|
|||
599 | -n, --no-status hide status prefix |
|
|||
600 | -0, --print0 end filenames with NUL, for use with xargs |
|
|||
601 | -I, --include <pat> include names matching the given patterns |
|
|||
602 | -X, --exclude <pat> exclude names matching the given patterns |
|
|||
603 |
|
||||
604 | tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]:: |
|
|||
605 | Name a particular revision using <name>. |
|
|||
606 |
|
||||
607 | Tags are used to name particular revisions of the repository and are |
|
|||
608 | very useful to compare different revision, to go back to significant |
|
|||
609 | earlier versions or to mark branch points as releases, etc. |
|
|||
610 |
|
||||
611 | If no revision is given, the tip is used. |
|
|||
612 |
|
||||
613 | To facilitate version control, distribution, and merging of tags, |
|
|||
614 | they are stored as a file named ".hgtags" which is managed |
|
|||
615 | similarly to other project files and can be hand-edited if |
|
|||
616 | necessary. |
|
|||
617 |
|
||||
618 | options: |
|
|||
619 | -l, --local make the tag local |
|
|||
620 | -m, --message <text> message for tag commit log entry |
|
|||
621 | -d, --date <datecode> datecode for commit |
|
|||
622 | -u, --user <user> user for commit |
|
|||
623 |
|
||||
624 | Note: Local tags are not version-controlled or distributed and are |
|
|||
625 | stored in the .hg/localtags file. If there exists a local tag and |
|
|||
626 | a public tag with the same name, local tag is used. |
|
|||
627 |
|
||||
628 | tags:: |
|
|||
629 | List the repository tags. |
|
|||
630 |
|
||||
631 | This lists both regular and local tags. |
|
|||
632 |
|
||||
633 | tip [-p]:: |
|
|||
634 | Show the tip revision. |
|
|||
635 |
|
||||
636 | options: |
|
|||
637 | -b, --branches show branches |
|
|||
638 | -p, --patch show patch |
|
|||
639 | --style <style> display using style map file |
|
|||
640 | --template <tpl> display using template |
|
|||
641 |
|
||||
642 | unbundle <file>:: |
|
|||
643 | (EXPERIMENTAL) |
|
|||
644 |
|
||||
645 | Apply a compressed changegroup file generated by the bundle |
|
|||
646 | command. |
|
|||
647 |
|
||||
648 | undo:: |
|
|||
649 | Undo the last commit or pull transaction. |
|
|||
650 |
|
||||
651 | Roll back the last pull or commit transaction on the |
|
|||
652 | repository, restoring the project to its earlier state. |
|
|||
653 |
|
||||
654 | This command should be used with care. There is only one level of |
|
|||
655 | undo and there is no redo. |
|
|||
656 |
|
||||
657 | This command is not intended for use on public repositories. Once |
|
|||
658 | a change is visible for pull by other users, undoing it locally is |
|
|||
659 | ineffective. |
|
|||
660 |
|
||||
661 | update [-m -C] [revision]:: |
|
|||
662 | Update the working directory to the specified revision. |
|
|||
663 |
|
||||
664 | By default, update will refuse to run if doing so would require |
|
|||
665 | merging or discarding local changes. |
|
|||
666 |
|
||||
667 | With the -m option, a merge will be performed. |
|
|||
668 |
|
||||
669 | With the -C option, local changes will be lost. |
|
|||
670 |
|
||||
671 | options: |
|
|||
672 | -m, --merge allow merging of branches |
|
|||
673 | -C, --clean overwrite locally modified files |
|
|||
674 |
|
||||
675 | aliases: up checkout co |
|
|||
676 |
|
||||
677 | verify:: |
|
|||
678 | Verify the integrity of the current repository. |
|
|||
679 |
|
||||
680 | This will perform an extensive check of the repository's |
|
|||
681 | integrity, validating the hashes and checksums of each entry in |
|
|||
682 | the changelog, manifest, and tracked files, as well as the |
|
|||
683 | integrity of their crosslinks and indices. |
|
|||
684 |
|
39 | |||
685 | FILE NAME PATTERNS |
|
40 | FILE NAME PATTERNS | |
686 | ------------------ |
|
41 | ------------------ |
@@ -1,11 +1,13 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | # bisect extension for mercurial | |
|
2 | # | |||
|
3 | # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org> | |||
|
4 | # Inspired by git bisect, extension skeleton taken from mq.py. | |||
2 | # |
|
5 | # | |
3 | # This software may be used and distributed according to the terms |
|
6 | # This software may be used and distributed according to the terms | |
4 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | # of the GNU General Public License, incorporated herein by reference. | |
5 |
|
8 | |||
6 | from mercurial.demandload import demandload |
|
9 | from mercurial.demandload import demandload | |
7 | demandload(globals(), "os sys sets") |
|
10 | demandload(globals(), "os sys sets mercurial:hg,util") | |
8 | from mercurial import hg |
|
|||
9 |
|
11 | |||
10 | versionstr = "0.0.3" |
|
12 | versionstr = "0.0.3" | |
11 |
|
13 | |||
@@ -30,33 +32,32 b' class bisect(object):' | |||||
30 | """dichotomic search in the DAG of changesets""" |
|
32 | """dichotomic search in the DAG of changesets""" | |
31 | def __init__(self, ui, repo): |
|
33 | def __init__(self, ui, repo): | |
32 | self.repo = repo |
|
34 | self.repo = repo | |
33 |
self.path = |
|
35 | self.path = repo.join("bisect") | |
|
36 | self.opener = util.opener(self.path) | |||
34 | self.ui = ui |
|
37 | self.ui = ui | |
35 | self.goodrevs = [] |
|
38 | self.goodrevs = [] | |
36 | self.badrev = None |
|
39 | self.badrev = None | |
37 | self.good_dirty = 0 |
|
40 | self.good_dirty = 0 | |
38 | self.bad_dirty = 0 |
|
41 | self.bad_dirty = 0 | |
39 |
self.good_path = |
|
42 | self.good_path = "good" | |
40 |
self.bad_path = |
|
43 | self.bad_path = "bad" | |
41 |
|
44 | |||
42 | s = self.good_path |
|
45 | if os.path.exists(os.path.join(self.path, self.good_path)): | |
43 | if os.path.exists(s): |
|
46 | self.goodrevs = self.opener(self.good_path).read().splitlines() | |
44 | self.goodrevs = self.repo.opener(s).read().splitlines() |
|
|||
45 | self.goodrevs = [hg.bin(x) for x in self.goodrevs] |
|
47 | self.goodrevs = [hg.bin(x) for x in self.goodrevs] | |
46 | s = self.bad_path |
|
48 | if os.path.exists(os.path.join(self.path, self.bad_path)): | |
47 | if os.path.exists(s): |
|
49 | r = self.opener(self.bad_path).read().splitlines() | |
48 | r = self.repo.opener(s).read().splitlines() |
|
|||
49 | if r: |
|
50 | if r: | |
50 | self.badrev = hg.bin(r.pop(0)) |
|
51 | self.badrev = hg.bin(r.pop(0)) | |
51 |
|
52 | |||
52 | def __del__(self): |
|
53 | def __del__(self): | |
53 | if not os.path.isdir(self.path): |
|
54 | if not os.path.isdir(self.path): | |
54 | return |
|
55 | return | |
55 |
f = self |
|
56 | f = self.opener(self.good_path, "w") | |
56 | f.write("\n".join([hg.hex(r) for r in self.goodrevs])) |
|
57 | f.write("\n".join([hg.hex(r) for r in self.goodrevs])) | |
57 | if len(self.goodrevs) > 0: |
|
58 | if len(self.goodrevs) > 0: | |
58 | f.write("\n") |
|
59 | f.write("\n") | |
59 |
f = self |
|
60 | f = self.opener(self.bad_path, "w") | |
60 | if self.badrev: |
|
61 | if self.badrev: | |
61 | f.write(hg.hex(self.badrev) + "\n") |
|
62 | f.write(hg.hex(self.badrev) + "\n") | |
62 |
|
63 | |||
@@ -72,7 +73,8 b' class bisect(object):' | |||||
72 | def reset(self): |
|
73 | def reset(self): | |
73 | """finish a bisection""" |
|
74 | """finish a bisection""" | |
74 | if os.path.isdir(self.path): |
|
75 | if os.path.isdir(self.path): | |
75 |
sl = [self. |
|
76 | sl = [os.path.join(self.path, p) | |
|
77 | for p in [self.bad_path, self.good_path]] | |||
76 | for s in sl: |
|
78 | for s in sl: | |
77 | if os.path.exists(s): |
|
79 | if os.path.exists(s): | |
78 | os.unlink(s) |
|
80 | os.unlink(s) | |
@@ -114,7 +116,8 b' class bisect(object):' | |||||
114 | cl = self.repo.changelog |
|
116 | cl = self.repo.changelog | |
115 | if not stop: |
|
117 | if not stop: | |
116 | stop = sets.Set([]) |
|
118 | stop = sets.Set([]) | |
117 |
for |
|
119 | for i in xrange(len(self.goodrevs)-1, -1, -1): | |
|
120 | g = self.goodrevs[i] | |||
118 | if g in stop: |
|
121 | if g in stop: | |
119 | continue |
|
122 | continue | |
120 | stop.update(cl.reachable(g)) |
|
123 | stop.update(cl.reachable(g)) | |
@@ -162,7 +165,8 b' class bisect(object):' | |||||
162 | if not self.goodrevs: |
|
165 | if not self.goodrevs: | |
163 | self.ui.warn("No good revision given\n") |
|
166 | self.ui.warn("No good revision given\n") | |
164 | self.ui.warn("Assuming the first revision is good\n") |
|
167 | self.ui.warn("Assuming the first revision is good\n") | |
165 |
ancestors, num_ancestors = self.__ancestors_and_nb_ancestors( |
|
168 | ancestors, num_ancestors = self.__ancestors_and_nb_ancestors( | |
|
169 | self.badrev) | |||
166 | tot = len(ancestors) |
|
170 | tot = len(ancestors) | |
167 | if tot == 1: |
|
171 | if tot == 1: | |
168 | if ancestors.pop() != self.badrev: |
|
172 | if ancestors.pop() != self.badrev: | |
@@ -281,7 +285,6 b' for subcommands see "hg bisect help\\"' | |||||
281 | return bisectcmdtable[cmd][0](*args) |
|
285 | return bisectcmdtable[cmd][0](*args) | |
282 |
|
286 | |||
283 | cmdtable = { |
|
287 | cmdtable = { | |
284 | "bisect": (bisect_run, [], |
|
288 | "bisect": (bisect_run, [], "hg bisect [help|init|reset|next|good|bad]"), | |
285 | "hg bisect [help|init|reset|next|good|bad]"), |
|
|||
286 | #"bisect-test": (test, [], "hg bisect-test rev"), |
|
289 | #"bisect-test": (test, [], "hg bisect-test rev"), | |
287 | } |
|
290 | } |
@@ -49,20 +49,11 b'' | |||||
49 | # to = recipient1, recipient2, ... |
|
49 | # to = recipient1, recipient2, ... | |
50 | # cc = cc1, cc2, ... |
|
50 | # cc = cc1, cc2, ... | |
51 |
|
51 | |||
52 | from email.MIMEMultipart import MIMEMultipart |
|
52 | from mercurial.demandload import * | |
53 | from email.MIMEText import MIMEText |
|
53 | demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils | |
54 | from email.Utils import parseaddr |
|
54 | mercurial:commands,hg,ui | |
55 | from mercurial import commands |
|
55 | os errno popen2 smtplib socket sys tempfile time''') | |
56 | from mercurial import hg |
|
|||
57 | from mercurial import ui |
|
|||
58 | from mercurial.i18n import gettext as _ |
|
56 | from mercurial.i18n import gettext as _ | |
59 | import os |
|
|||
60 | import popen2 |
|
|||
61 | import smtplib |
|
|||
62 | import socket |
|
|||
63 | import sys |
|
|||
64 | import tempfile |
|
|||
65 | import time |
|
|||
66 |
|
57 | |||
67 | try: |
|
58 | try: | |
68 | # readline gives raw_input editing capabilities, but is not |
|
59 | # readline gives raw_input editing capabilities, but is not | |
@@ -149,7 +140,10 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
149 | if opts['diffstat']: |
|
140 | if opts['diffstat']: | |
150 | body += cdiffstat('\n'.join(desc), patch) + '\n\n' |
|
141 | body += cdiffstat('\n'.join(desc), patch) + '\n\n' | |
151 | body += '\n'.join(patch) |
|
142 | body += '\n'.join(patch) | |
152 | msg = MIMEText(body) |
|
143 | msg = email.MIMEText.MIMEText(body) | |
|
144 | if total == 1: | |||
|
145 | subj = '[PATCH] ' + desc[0].strip() | |||
|
146 | else: | |||
153 | subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip()) |
|
147 | subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip()) | |
154 | if subj.endswith('.'): subj = subj[:-1] |
|
148 | if subj.endswith('.'): subj = subj[:-1] | |
155 | msg['Subject'] = subj |
|
149 | msg['Subject'] = subj | |
@@ -189,17 +183,9 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
189 | jumbo.extend(p) |
|
183 | jumbo.extend(p) | |
190 | msgs.append(makepatch(p, i + 1, len(patches))) |
|
184 | msgs.append(makepatch(p, i + 1, len(patches))) | |
191 |
|
185 | |||
192 | ui.write(_('\nWrite the introductory message for the patch series.\n\n')) |
|
|||
193 |
|
||||
194 | sender = (opts['from'] or ui.config('patchbomb', 'from') or |
|
186 | sender = (opts['from'] or ui.config('patchbomb', 'from') or | |
195 | prompt('From', ui.username())) |
|
187 | prompt('From', ui.username())) | |
196 |
|
188 | |||
197 | msg = MIMEMultipart() |
|
|||
198 | msg['Subject'] = '[PATCH 0 of %d] %s' % ( |
|
|||
199 | len(patches), |
|
|||
200 | opts['subject'] or |
|
|||
201 | prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches))) |
|
|||
202 |
|
||||
203 | def getaddrs(opt, prpt, default = None): |
|
189 | def getaddrs(opt, prpt, default = None): | |
204 | addrs = opts[opt] or (ui.config('patchbomb', opt) or |
|
190 | addrs = opts[opt] or (ui.config('patchbomb', opt) or | |
205 | prompt(prpt, default = default)).split(',') |
|
191 | prompt(prpt, default = default)).split(',') | |
@@ -207,6 +193,15 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
207 | to = getaddrs('to', 'To') |
|
193 | to = getaddrs('to', 'To') | |
208 | cc = getaddrs('cc', 'Cc', '') |
|
194 | cc = getaddrs('cc', 'Cc', '') | |
209 |
|
195 | |||
|
196 | if len(patches) > 1: | |||
|
197 | ui.write(_('\nWrite the introductory message for the patch series.\n\n')) | |||
|
198 | ||||
|
199 | msg = email.MIMEMultipart.MIMEMultipart() | |||
|
200 | msg['Subject'] = '[PATCH 0 of %d] %s' % ( | |||
|
201 | len(patches), | |||
|
202 | opts['subject'] or | |||
|
203 | prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches))) | |||
|
204 | ||||
210 | ui.write(_('Finish with ^D or a dot on a line by itself.\n\n')) |
|
205 | ui.write(_('Finish with ^D or a dot on a line by itself.\n\n')) | |
211 |
|
206 | |||
212 | body = [] |
|
207 | body = [] | |
@@ -217,16 +212,16 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
217 | if l == '.': break |
|
212 | if l == '.': break | |
218 | body.append(l) |
|
213 | body.append(l) | |
219 |
|
214 | |||
220 | msg.attach(MIMEText('\n'.join(body) + '\n')) |
|
215 | msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n')) | |
221 |
|
||||
222 | ui.write('\n') |
|
|||
223 |
|
216 | |||
224 | if opts['diffstat']: |
|
217 | if opts['diffstat']: | |
225 | d = cdiffstat(_('Final summary:\n'), jumbo) |
|
218 | d = cdiffstat(_('Final summary:\n'), jumbo) | |
226 | if d: msg.attach(MIMEText(d)) |
|
219 | if d: msg.attach(email.MIMEText.MIMEText(d)) | |
227 |
|
220 | |||
228 | msgs.insert(0, msg) |
|
221 | msgs.insert(0, msg) | |
229 |
|
222 | |||
|
223 | ui.write('\n') | |||
|
224 | ||||
230 | if not opts['test'] and not opts['mbox']: |
|
225 | if not opts['test'] and not opts['mbox']: | |
231 | s = smtplib.SMTP() |
|
226 | s = smtplib.SMTP() | |
232 | s.connect(host = ui.config('smtp', 'host', 'mail'), |
|
227 | s.connect(host = ui.config('smtp', 'host', 'mail'), | |
@@ -241,7 +236,7 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
241 | s.login(username, password) |
|
236 | s.login(username, password) | |
242 | parent = None |
|
237 | parent = None | |
243 | tz = time.strftime('%z') |
|
238 | tz = time.strftime('%z') | |
244 | sender_addr = parseaddr(sender)[1] |
|
239 | sender_addr = email.Utils.parseaddr(sender)[1] | |
245 | for m in msgs: |
|
240 | for m in msgs: | |
246 | try: |
|
241 | try: | |
247 | m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) |
|
242 | m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) | |
@@ -259,8 +254,12 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
259 | if opts['test']: |
|
254 | if opts['test']: | |
260 | ui.status('Displaying ', m['Subject'], ' ...\n') |
|
255 | ui.status('Displaying ', m['Subject'], ' ...\n') | |
261 | fp = os.popen(os.getenv('PAGER', 'more'), 'w') |
|
256 | fp = os.popen(os.getenv('PAGER', 'more'), 'w') | |
|
257 | try: | |||
262 | fp.write(m.as_string(0)) |
|
258 | fp.write(m.as_string(0)) | |
263 | fp.write('\n') |
|
259 | fp.write('\n') | |
|
260 | except IOError, inst: | |||
|
261 | if inst.errno != errno.EPIPE: | |||
|
262 | raise | |||
264 | fp.close() |
|
263 | fp.close() | |
265 | elif opts['mbox']: |
|
264 | elif opts['mbox']: | |
266 | ui.status('Writing ', m['Subject'], ' ...\n') |
|
265 | ui.status('Writing ', m['Subject'], ' ...\n') |
@@ -3,7 +3,13 b'' | |||||
3 | # hgmerge - default merge helper for Mercurial |
|
3 | # hgmerge - default merge helper for Mercurial | |
4 | # |
|
4 | # | |
5 | # This tries to find a way to do three-way merge on the current system. |
|
5 | # This tries to find a way to do three-way merge on the current system. | |
6 | # The result ought to end up in $1. |
|
6 | # The result ought to end up in $1. Script is run in root directory of | |
|
7 | # repository. | |||
|
8 | # | |||
|
9 | # Environment variables set by Mercurial: | |||
|
10 | # HG_FILE name of file within repo | |||
|
11 | # HG_MY_NODE revision being merged | |||
|
12 | # HG_OTHER_NODE revision being merged | |||
7 |
|
13 | |||
8 | set -e # bail out quickly on failure |
|
14 | set -e # bail out quickly on failure | |
9 |
|
15 |
@@ -8,10 +8,21 b' cgitb.enable()' | |||||
8 | # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install |
|
8 | # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install | |
9 | from mercurial import hgweb |
|
9 | from mercurial import hgweb | |
10 |
|
10 | |||
11 |
# The config file looks like this |
|
11 | # The config file looks like this. You can have paths to individual | |
|
12 | # repos, collections of repos in a directory tree, or both. | |||
|
13 | # | |||
12 | # [paths] |
|
14 | # [paths] | |
13 | # virtual/path = /real/path |
|
15 | # virtual/path = /real/path | |
14 | # virtual/path = /real/path |
|
16 | # virtual/path = /real/path | |
|
17 | # | |||
|
18 | # [collections] | |||
|
19 | # /prefix/to/strip/off = /root/of/tree/full/of/repos | |||
|
20 | # | |||
|
21 | # collections example: say directory tree /foo contains repos /foo/bar, | |||
|
22 | # /foo/quux/baz. Give this config section: | |||
|
23 | # [collections] | |||
|
24 | # /foo = /foo | |||
|
25 | # Then repos will list as bar and quux/baz. | |||
15 |
|
26 | |||
16 | # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples |
|
27 | # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples | |
17 | # or use a dictionary with entries like 'virtual/path': '/real/path' |
|
28 | # or use a dictionary with entries like 'virtual/path': '/real/path' |
@@ -276,6 +276,14 b' def make_file(repo, r, pat, node=None,' | |||||
276 |
|
276 | |||
277 | def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always, |
|
277 | def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always, | |
278 | changes=None, text=False, opts={}): |
|
278 | changes=None, text=False, opts={}): | |
|
279 | if not node1: | |||
|
280 | node1 = repo.dirstate.parents()[0] | |||
|
281 | # reading the data for node1 early allows it to play nicely | |||
|
282 | # with repo.changes and the revlog cache. | |||
|
283 | change = repo.changelog.read(node1) | |||
|
284 | mmap = repo.manifest.read(change[0]) | |||
|
285 | date1 = util.datestr(change[2]) | |||
|
286 | ||||
279 | if not changes: |
|
287 | if not changes: | |
280 | changes = repo.changes(node1, node2, files, match=match) |
|
288 | changes = repo.changes(node1, node2, files, match=match) | |
281 | modified, added, removed, deleted, unknown = changes |
|
289 | modified, added, removed, deleted, unknown = changes | |
@@ -294,8 +302,6 b' def dodiff(fp, ui, repo, node1, node2, f' | |||||
294 | return repo.file(f).read(mmap2[f]) |
|
302 | return repo.file(f).read(mmap2[f]) | |
295 | else: |
|
303 | else: | |
296 | date2 = util.datestr() |
|
304 | date2 = util.datestr() | |
297 | if not node1: |
|
|||
298 | node1 = repo.dirstate.parents()[0] |
|
|||
299 | def read(f): |
|
305 | def read(f): | |
300 | return repo.wread(f) |
|
306 | return repo.wread(f) | |
301 |
|
307 | |||
@@ -305,10 +311,6 b' def dodiff(fp, ui, repo, node1, node2, f' | |||||
305 | hexfunc = ui.verbose and hex or short |
|
311 | hexfunc = ui.verbose and hex or short | |
306 | r = [hexfunc(node) for node in [node1, node2] if node] |
|
312 | r = [hexfunc(node) for node in [node1, node2] if node] | |
307 |
|
313 | |||
308 | change = repo.changelog.read(node1) |
|
|||
309 | mmap = repo.manifest.read(change[0]) |
|
|||
310 | date1 = util.datestr(change[2]) |
|
|||
311 |
|
||||
312 | diffopts = ui.diffopts() |
|
314 | diffopts = ui.diffopts() | |
313 | showfunc = opts.get('show_function') or diffopts['showfunc'] |
|
315 | showfunc = opts.get('show_function') or diffopts['showfunc'] | |
314 | ignorews = opts.get('ignore_all_space') or diffopts['ignorews'] |
|
316 | ignorews = opts.get('ignore_all_space') or diffopts['ignorews'] | |
@@ -793,12 +795,7 b' def annotate(ui, repo, *pats, **opts):' | |||||
793 | change = repo.changelog.read(node) |
|
795 | change = repo.changelog.read(node) | |
794 | mmap = repo.manifest.read(change[0]) |
|
796 | mmap = repo.manifest.read(change[0]) | |
795 |
|
797 | |||
796 | for src, abs, rel, exact in walk(repo, pats, opts): |
|
798 | for src, abs, rel, exact in walk(repo, pats, opts, node=node): | |
797 | if abs not in mmap: |
|
|||
798 | ui.warn(_("warning: %s is not in the repository!\n") % |
|
|||
799 | ((pats and rel) or abs)) |
|
|||
800 | continue |
|
|||
801 |
|
||||
802 | f = repo.file(abs) |
|
799 | f = repo.file(abs) | |
803 | if not opts['text'] and util.binary(f.read(mmap[abs])): |
|
800 | if not opts['text'] and util.binary(f.read(mmap[abs])): | |
804 | ui.write(_("%s: binary file\n") % ((pats and rel) or abs)) |
|
801 | ui.write(_("%s: binary file\n") % ((pats and rel) or abs)) | |
@@ -834,7 +831,7 b' def bundle(ui, repo, fname, dest="defaul' | |||||
834 | contents including permissions, rename data, and revision history. |
|
831 | contents including permissions, rename data, and revision history. | |
835 | """ |
|
832 | """ | |
836 | f = open(fname, "wb") |
|
833 | f = open(fname, "wb") | |
837 |
dest = ui.expandpath(dest |
|
834 | dest = ui.expandpath(dest) | |
838 | other = hg.repository(ui, dest) |
|
835 | other = hg.repository(ui, dest) | |
839 | o = repo.findoutgoing(other) |
|
836 | o = repo.findoutgoing(other) | |
840 | cg = repo.changegroup(o, 'bundle') |
|
837 | cg = repo.changegroup(o, 'bundle') | |
@@ -896,6 +893,8 b' def clone(ui, source, dest=None, **opts)' | |||||
896 | such as AFS, implement hardlinking incorrectly, but do not report |
|
893 | such as AFS, implement hardlinking incorrectly, but do not report | |
897 | errors. In these cases, use the --pull option to avoid |
|
894 | errors. In these cases, use the --pull option to avoid | |
898 | hardlinking. |
|
895 | hardlinking. | |
|
896 | ||||
|
897 | See pull for valid source format details. | |||
899 | """ |
|
898 | """ | |
900 | if dest is None: |
|
899 | if dest is None: | |
901 | dest = os.path.basename(os.path.normpath(source)) |
|
900 | dest = os.path.basename(os.path.normpath(source)) | |
@@ -921,7 +920,6 b' def clone(ui, source, dest=None, **opts)' | |||||
921 | if opts['remotecmd']: |
|
920 | if opts['remotecmd']: | |
922 | ui.setconfig("ui", "remotecmd", opts['remotecmd']) |
|
921 | ui.setconfig("ui", "remotecmd", opts['remotecmd']) | |
923 |
|
922 | |||
924 | if not os.path.exists(source): |
|
|||
925 |
|
|
923 | source = ui.expandpath(source) | |
926 |
|
924 | |||
927 | d = Dircleanup(dest) |
|
925 | d = Dircleanup(dest) | |
@@ -978,7 +976,7 b' def clone(ui, source, dest=None, **opts)' | |||||
978 | f.close() |
|
976 | f.close() | |
979 |
|
977 | |||
980 | if not opts['noupdate']: |
|
978 | if not opts['noupdate']: | |
981 | update(ui, repo) |
|
979 | update(repo.ui, repo) | |
982 |
|
980 | |||
983 | d.close() |
|
981 | d.close() | |
984 |
|
982 | |||
@@ -1023,7 +1021,8 b' def commit(ui, repo, *pats, **opts):' | |||||
1023 | except ValueError, inst: |
|
1021 | except ValueError, inst: | |
1024 | raise util.Abort(str(inst)) |
|
1022 | raise util.Abort(str(inst)) | |
1025 |
|
1023 | |||
1026 | def docopy(ui, repo, pats, opts): |
|
1024 | def docopy(ui, repo, pats, opts, wlock): | |
|
1025 | # called with the repo lock held | |||
1027 | cwd = repo.getcwd() |
|
1026 | cwd = repo.getcwd() | |
1028 | errors = 0 |
|
1027 | errors = 0 | |
1029 | copied = [] |
|
1028 | copied = [] | |
@@ -1069,8 +1068,16 b' def docopy(ui, repo, pats, opts):' | |||||
1069 | if not os.path.isdir(targetdir): |
|
1068 | if not os.path.isdir(targetdir): | |
1070 | os.makedirs(targetdir) |
|
1069 | os.makedirs(targetdir) | |
1071 | try: |
|
1070 | try: | |
|
1071 | restore = repo.dirstate.state(abstarget) == 'r' | |||
|
1072 | if restore: | |||
|
1073 | repo.undelete([abstarget], wlock) | |||
|
1074 | try: | |||
1072 | shutil.copyfile(relsrc, reltarget) |
|
1075 | shutil.copyfile(relsrc, reltarget) | |
1073 | shutil.copymode(relsrc, reltarget) |
|
1076 | shutil.copymode(relsrc, reltarget) | |
|
1077 | restore = False | |||
|
1078 | finally: | |||
|
1079 | if restore: | |||
|
1080 | repo.remove([abstarget], wlock) | |||
1074 | except shutil.Error, inst: |
|
1081 | except shutil.Error, inst: | |
1075 | raise util.Abort(str(inst)) |
|
1082 | raise util.Abort(str(inst)) | |
1076 | except IOError, inst: |
|
1083 | except IOError, inst: | |
@@ -1084,7 +1091,8 b' def docopy(ui, repo, pats, opts):' | |||||
1084 | if ui.verbose or not exact: |
|
1091 | if ui.verbose or not exact: | |
1085 | ui.status(_('copying %s to %s\n') % (relsrc, reltarget)) |
|
1092 | ui.status(_('copying %s to %s\n') % (relsrc, reltarget)) | |
1086 | targets[abstarget] = abssrc |
|
1093 | targets[abstarget] = abssrc | |
1087 | repo.copy(origsrc, abstarget) |
|
1094 | if abstarget != origsrc: | |
|
1095 | repo.copy(origsrc, abstarget, wlock) | |||
1088 | copied.append((abssrc, relsrc, exact)) |
|
1096 | copied.append((abssrc, relsrc, exact)) | |
1089 |
|
1097 | |||
1090 | def targetpathfn(pat, dest, srcs): |
|
1098 | def targetpathfn(pat, dest, srcs): | |
@@ -1192,15 +1200,26 b' def copy(ui, repo, *pats, **opts):' | |||||
1192 | should properly record copied files, this information is not yet |
|
1200 | should properly record copied files, this information is not yet | |
1193 | fully used by merge, nor fully reported by log. |
|
1201 | fully used by merge, nor fully reported by log. | |
1194 | """ |
|
1202 | """ | |
1195 | errs, copied = docopy(ui, repo, pats, opts) |
|
1203 | try: | |
|
1204 | wlock = repo.wlock(0) | |||
|
1205 | errs, copied = docopy(ui, repo, pats, opts, wlock) | |||
|
1206 | except lock.LockHeld, inst: | |||
|
1207 | ui.warn(_("repository lock held by %s\n") % inst.args[0]) | |||
|
1208 | errs = 1 | |||
1196 | return errs |
|
1209 | return errs | |
1197 |
|
1210 | |||
1198 | def debugancestor(ui, index, rev1, rev2): |
|
1211 | def debugancestor(ui, index, rev1, rev2): | |
1199 | """find the ancestor revision of two revisions in a given index""" |
|
1212 | """find the ancestor revision of two revisions in a given index""" | |
1200 | r = revlog.revlog(util.opener(os.getcwd()), index, "") |
|
1213 | r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "") | |
1201 | a = r.ancestor(r.lookup(rev1), r.lookup(rev2)) |
|
1214 | a = r.ancestor(r.lookup(rev1), r.lookup(rev2)) | |
1202 | ui.write("%d:%s\n" % (r.rev(a), hex(a))) |
|
1215 | ui.write("%d:%s\n" % (r.rev(a), hex(a))) | |
1203 |
|
1216 | |||
|
1217 | def debugcomplete(ui, cmd): | |||
|
1218 | """returns the completion list associated with the given command""" | |||
|
1219 | clist = findpossible(cmd).keys() | |||
|
1220 | clist.sort() | |||
|
1221 | ui.write("%s\n" % " ".join(clist)) | |||
|
1222 | ||||
1204 | def debugrebuildstate(ui, repo, rev=None): |
|
1223 | def debugrebuildstate(ui, repo, rev=None): | |
1205 | """rebuild the dirstate as it would look like for the given revision""" |
|
1224 | """rebuild the dirstate as it would look like for the given revision""" | |
1206 | if not rev: |
|
1225 | if not rev: | |
@@ -1246,12 +1265,8 b' def debugcheckstate(ui, repo):' | |||||
1246 | error = _(".hg/dirstate inconsistent with current parent's manifest") |
|
1265 | error = _(".hg/dirstate inconsistent with current parent's manifest") | |
1247 | raise util.Abort(error) |
|
1266 | raise util.Abort(error) | |
1248 |
|
1267 | |||
1249 | def debugconfig(ui): |
|
1268 | def debugconfig(ui, repo): | |
1250 | """show combined config settings from all hgrc files""" |
|
1269 | """show combined config settings from all hgrc files""" | |
1251 | try: |
|
|||
1252 | repo = hg.repository(ui) |
|
|||
1253 | except hg.RepoError: |
|
|||
1254 | pass |
|
|||
1255 | for section, name, value in ui.walkconfig(): |
|
1270 | for section, name, value in ui.walkconfig(): | |
1256 | ui.write('%s.%s=%s\n' % (section, name, value)) |
|
1271 | ui.write('%s.%s=%s\n' % (section, name, value)) | |
1257 |
|
1272 | |||
@@ -1283,7 +1298,8 b' def debugstate(ui, repo):' | |||||
1283 |
|
1298 | |||
1284 | def debugdata(ui, file_, rev): |
|
1299 | def debugdata(ui, file_, rev): | |
1285 | """dump the contents of an data file revision""" |
|
1300 | """dump the contents of an data file revision""" | |
1286 |
r = revlog.revlog(util.opener(os.getcwd()), |
|
1301 | r = revlog.revlog(util.opener(os.getcwd(), audit=False), | |
|
1302 | file_[:-2] + ".i", file_) | |||
1287 | try: |
|
1303 | try: | |
1288 | ui.write(r.revision(r.lookup(rev))) |
|
1304 | ui.write(r.revision(r.lookup(rev))) | |
1289 | except KeyError: |
|
1305 | except KeyError: | |
@@ -1291,7 +1307,7 b' def debugdata(ui, file_, rev):' | |||||
1291 |
|
1307 | |||
1292 | def debugindex(ui, file_): |
|
1308 | def debugindex(ui, file_): | |
1293 | """dump the contents of an index file""" |
|
1309 | """dump the contents of an index file""" | |
1294 | r = revlog.revlog(util.opener(os.getcwd()), file_, "") |
|
1310 | r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "") | |
1295 | ui.write(" rev offset length base linkrev" + |
|
1311 | ui.write(" rev offset length base linkrev" + | |
1296 | " nodeid p1 p2\n") |
|
1312 | " nodeid p1 p2\n") | |
1297 | for i in range(r.count()): |
|
1313 | for i in range(r.count()): | |
@@ -1302,7 +1318,7 b' def debugindex(ui, file_):' | |||||
1302 |
|
1318 | |||
1303 | def debugindexdot(ui, file_): |
|
1319 | def debugindexdot(ui, file_): | |
1304 | """dump an index DAG as a .dot file""" |
|
1320 | """dump an index DAG as a .dot file""" | |
1305 | r = revlog.revlog(util.opener(os.getcwd()), file_, "") |
|
1321 | r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "") | |
1306 | ui.write("digraph G {\n") |
|
1322 | ui.write("digraph G {\n") | |
1307 | for i in range(r.count()): |
|
1323 | for i in range(r.count()): | |
1308 | e = r.index[i] |
|
1324 | e = r.index[i] | |
@@ -1730,7 +1746,7 b' def incoming(ui, repo, source="default",' | |||||
1730 |
|
1746 | |||
1731 | Currently only local repositories are supported. |
|
1747 | Currently only local repositories are supported. | |
1732 | """ |
|
1748 | """ | |
1733 |
source = ui.expandpath(source |
|
1749 | source = ui.expandpath(source) | |
1734 | other = hg.repository(ui, source) |
|
1750 | other = hg.repository(ui, source) | |
1735 | if not other.local(): |
|
1751 | if not other.local(): | |
1736 | raise util.Abort(_("incoming doesn't work for remote repositories yet")) |
|
1752 | raise util.Abort(_("incoming doesn't work for remote repositories yet")) | |
@@ -1917,8 +1933,10 b' def outgoing(ui, repo, dest="default-pus' | |||||
1917 | Show changesets not found in the specified destination repo or the |
|
1933 | Show changesets not found in the specified destination repo or the | |
1918 | default push repo. These are the changesets that would be pushed |
|
1934 | default push repo. These are the changesets that would be pushed | |
1919 | if a push was requested. |
|
1935 | if a push was requested. | |
|
1936 | ||||
|
1937 | See pull for valid source format details. | |||
1920 | """ |
|
1938 | """ | |
1921 |
dest = ui.expandpath(dest |
|
1939 | dest = ui.expandpath(dest) | |
1922 | other = hg.repository(ui, dest) |
|
1940 | other = hg.repository(ui, dest) | |
1923 | o = repo.findoutgoing(other) |
|
1941 | o = repo.findoutgoing(other) | |
1924 | o = repo.changelog.nodesbetween(o)[0] |
|
1942 | o = repo.changelog.nodesbetween(o)[0] | |
@@ -1953,7 +1971,7 b' def parents(ui, repo, rev=None, branches' | |||||
1953 | if n != nullid: |
|
1971 | if n != nullid: | |
1954 | displayer.show(changenode=n, brinfo=br) |
|
1972 | displayer.show(changenode=n, brinfo=br) | |
1955 |
|
1973 | |||
1956 | def paths(ui, search=None): |
|
1974 | def paths(ui, repo, search=None): | |
1957 | """show definition of symbolic path names |
|
1975 | """show definition of symbolic path names | |
1958 |
|
1976 | |||
1959 | Show definition of symbolic path name NAME. If no name is given, show |
|
1977 | Show definition of symbolic path name NAME. If no name is given, show | |
@@ -1962,11 +1980,6 b' def paths(ui, search=None):' | |||||
1962 | Path names are defined in the [paths] section of /etc/mercurial/hgrc |
|
1980 | Path names are defined in the [paths] section of /etc/mercurial/hgrc | |
1963 | and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too. |
|
1981 | and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too. | |
1964 | """ |
|
1982 | """ | |
1965 | try: |
|
|||
1966 | repo = hg.repository(ui=ui) |
|
|||
1967 | except hg.RepoError: |
|
|||
1968 | pass |
|
|||
1969 |
|
||||
1970 | if search: |
|
1983 | if search: | |
1971 | for name, path in ui.configitems("paths"): |
|
1984 | for name, path in ui.configitems("paths"): | |
1972 | if name == search: |
|
1985 | if name == search: | |
@@ -1999,7 +2012,7 b' def pull(ui, repo, source="default", **o' | |||||
1999 | to the remote user's home directory by default; use two slashes at |
|
2012 | to the remote user's home directory by default; use two slashes at | |
2000 | the start of a path to specify it as relative to the filesystem root. |
|
2013 | the start of a path to specify it as relative to the filesystem root. | |
2001 | """ |
|
2014 | """ | |
2002 |
source = ui.expandpath(source |
|
2015 | source = ui.expandpath(source) | |
2003 | ui.status(_('pulling from %s\n') % (source)) |
|
2016 | ui.status(_('pulling from %s\n') % (source)) | |
2004 |
|
2017 | |||
2005 | if opts['ssh']: |
|
2018 | if opts['ssh']: | |
@@ -2044,7 +2057,7 b' def push(ui, repo, dest="default-push", ' | |||||
2044 | SSH requires an accessible shell account on the destination |
|
2057 | SSH requires an accessible shell account on the destination | |
2045 | machine and a copy of hg in the remote path. |
|
2058 | machine and a copy of hg in the remote path. | |
2046 | """ |
|
2059 | """ | |
2047 |
dest = ui.expandpath(dest |
|
2060 | dest = ui.expandpath(dest) | |
2048 | ui.status('pushing to %s\n' % (dest)) |
|
2061 | ui.status('pushing to %s\n' % (dest)) | |
2049 |
|
2062 | |||
2050 | if opts['ssh']: |
|
2063 | if opts['ssh']: | |
@@ -2062,6 +2075,7 b' def push(ui, repo, dest="default-push", ' | |||||
2062 | def rawcommit(ui, repo, *flist, **rc): |
|
2075 | def rawcommit(ui, repo, *flist, **rc): | |
2063 | """raw commit interface (DEPRECATED) |
|
2076 | """raw commit interface (DEPRECATED) | |
2064 |
|
2077 | |||
|
2078 | (DEPRECATED) | |||
2065 | Lowlevel commit, for use in helper scripts. |
|
2079 | Lowlevel commit, for use in helper scripts. | |
2066 |
|
2080 | |||
2067 | This command is not intended to be used by normal users, as it is |
|
2081 | This command is not intended to be used by normal users, as it is | |
@@ -2119,7 +2133,7 b' def remove(ui, repo, pat, *pats, **opts)' | |||||
2119 | def okaytoremove(abs, rel, exact): |
|
2133 | def okaytoremove(abs, rel, exact): | |
2120 | modified, added, removed, deleted, unknown = repo.changes(files=[abs]) |
|
2134 | modified, added, removed, deleted, unknown = repo.changes(files=[abs]) | |
2121 | reason = None |
|
2135 | reason = None | |
2122 | if modified: |
|
2136 | if modified and not opts['force']: | |
2123 | reason = _('is modified') |
|
2137 | reason = _('is modified') | |
2124 | elif added: |
|
2138 | elif added: | |
2125 | reason = _('has been marked for add') |
|
2139 | reason = _('has been marked for add') | |
@@ -2154,21 +2168,33 b' def rename(ui, repo, *pats, **opts):' | |||||
2154 | should properly record rename files, this information is not yet |
|
2168 | should properly record rename files, this information is not yet | |
2155 | fully used by merge, nor fully reported by log. |
|
2169 | fully used by merge, nor fully reported by log. | |
2156 | """ |
|
2170 | """ | |
2157 | errs, copied = docopy(ui, repo, pats, opts) |
|
2171 | try: | |
|
2172 | wlock = repo.wlock(0) | |||
|
2173 | errs, copied = docopy(ui, repo, pats, opts, wlock) | |||
2158 | names = [] |
|
2174 | names = [] | |
2159 | for abs, rel, exact in copied: |
|
2175 | for abs, rel, exact in copied: | |
2160 | if ui.verbose or not exact: |
|
2176 | if ui.verbose or not exact: | |
2161 | ui.status(_('removing %s\n') % rel) |
|
2177 | ui.status(_('removing %s\n') % rel) | |
2162 | names.append(abs) |
|
2178 | names.append(abs) | |
2163 |
repo.remove(names, |
|
2179 | repo.remove(names, True, wlock) | |
|
2180 | except lock.LockHeld, inst: | |||
|
2181 | ui.warn(_("repository lock held by %s\n") % inst.args[0]) | |||
|
2182 | errs = 1 | |||
2164 | return errs |
|
2183 | return errs | |
2165 |
|
2184 | |||
2166 | def revert(ui, repo, *pats, **opts): |
|
2185 | def revert(ui, repo, *pats, **opts): | |
2167 | """revert modified files or dirs back to their unmodified states |
|
2186 | """revert modified files or dirs back to their unmodified states | |
2168 |
|
2187 | |||
2169 |
|
|
2188 | In its default mode, it reverts any uncommitted modifications made | |
2170 |
directories. This restores the contents of |
|
2189 | to the named files or directories. This restores the contents of | |
2171 | an unmodified state. |
|
2190 | the affected files to an unmodified state. | |
|
2191 | ||||
|
2192 | Using the -r option, it reverts the given files or directories to | |||
|
2193 | their state as of an earlier revision. This can be helpful to "roll | |||
|
2194 | back" some or all of a change that should not have been committed. | |||
|
2195 | ||||
|
2196 | Revert modifies the working directory. It does not commit any | |||
|
2197 | changes, or change the parent of the current working directory. | |||
2172 |
|
2198 | |||
2173 | If a file has been deleted, it is recreated. If the executable |
|
2199 | If a file has been deleted, it is recreated. If the executable | |
2174 | mode of a file was changed, it is reset. |
|
2200 | mode of a file was changed, it is reset. | |
@@ -2183,7 +2209,7 b' def revert(ui, repo, *pats, **opts):' | |||||
2183 | files, choose, anypats = matchpats(repo, pats, opts) |
|
2209 | files, choose, anypats = matchpats(repo, pats, opts) | |
2184 | modified, added, removed, deleted, unknown = repo.changes(match=choose) |
|
2210 | modified, added, removed, deleted, unknown = repo.changes(match=choose) | |
2185 | repo.forget(added) |
|
2211 | repo.forget(added) | |
2186 |
repo.undelete(removed |
|
2212 | repo.undelete(removed) | |
2187 |
|
2213 | |||
2188 | return repo.update(node, False, True, choose, False) |
|
2214 | return repo.update(node, False, True, choose, False) | |
2189 |
|
2215 | |||
@@ -2573,50 +2599,51 b' table = {' | |||||
2573 | ('c', 'changeset', None, _('list the changeset')), |
|
2599 | ('c', 'changeset', None, _('list the changeset')), | |
2574 | ('I', 'include', [], _('include names matching the given patterns')), |
|
2600 | ('I', 'include', [], _('include names matching the given patterns')), | |
2575 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], |
|
2601 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |
2576 |
_('hg annotate [ |
|
2602 | _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')), | |
2577 | "bundle": |
|
2603 | "bundle": | |
2578 | (bundle, |
|
2604 | (bundle, | |
2579 | [], |
|
2605 | [], | |
2580 | _('hg bundle FILE DEST')), |
|
2606 | _('hg bundle FILE DEST')), | |
2581 | "cat": |
|
2607 | "cat": | |
2582 | (cat, |
|
2608 | (cat, | |
2583 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2609 | [('o', 'output', '', _('print output to file with formatted name')), | |
2584 |
(' |
|
2610 | ('r', 'rev', '', _('print the given revision')), | |
2585 | ('o', 'output', '', _('print output to file with formatted name')), |
|
2611 | ('I', 'include', [], _('include names matching the given patterns')), | |
2586 |
(' |
|
2612 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |
2587 | _('hg cat [OPTION]... FILE...')), |
|
2613 | _('hg cat [OPTION]... FILE...')), | |
2588 | "^clone": |
|
2614 | "^clone": | |
2589 | (clone, |
|
2615 | (clone, | |
2590 | [('U', 'noupdate', None, _('do not update the new working directory')), |
|
2616 | [('U', 'noupdate', None, _('do not update the new working directory')), | |
2591 | ('e', 'ssh', '', _('specify ssh command to use')), |
|
|||
2592 | ('', 'pull', None, _('use pull protocol to copy metadata')), |
|
|||
2593 | ('r', 'rev', [], |
|
2617 | ('r', 'rev', [], | |
2594 | _('a changeset you would like to have after cloning')), |
|
2618 | _('a changeset you would like to have after cloning')), | |
|
2619 | ('', 'pull', None, _('use pull protocol to copy metadata')), | |||
|
2620 | ('e', 'ssh', '', _('specify ssh command to use')), | |||
2595 | ('', 'remotecmd', '', |
|
2621 | ('', 'remotecmd', '', | |
2596 | _('specify hg command to run on the remote side'))], |
|
2622 | _('specify hg command to run on the remote side'))], | |
2597 | _('hg clone [OPTION]... SOURCE [DEST]')), |
|
2623 | _('hg clone [OPTION]... SOURCE [DEST]')), | |
2598 | "^commit|ci": |
|
2624 | "^commit|ci": | |
2599 | (commit, |
|
2625 | (commit, | |
2600 | [('A', 'addremove', None, _('run addremove during commit')), |
|
2626 | [('A', 'addremove', None, _('run addremove during commit')), | |
2601 | ('I', 'include', [], _('include names matching the given patterns')), |
|
|||
2602 | ('X', 'exclude', [], _('exclude names matching the given patterns')), |
|
|||
2603 | ('m', 'message', '', _('use <text> as commit message')), |
|
2627 | ('m', 'message', '', _('use <text> as commit message')), | |
2604 | ('l', 'logfile', '', _('read the commit message from <file>')), |
|
2628 | ('l', 'logfile', '', _('read the commit message from <file>')), | |
2605 | ('d', 'date', '', _('record datecode as commit date')), |
|
2629 | ('d', 'date', '', _('record datecode as commit date')), | |
2606 |
('u', 'user', '', _('record user as commiter')) |
|
2630 | ('u', 'user', '', _('record user as commiter')), | |
|
2631 | ('I', 'include', [], _('include names matching the given patterns')), | |||
|
2632 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |||
2607 | _('hg commit [OPTION]... [FILE]...')), |
|
2633 | _('hg commit [OPTION]... [FILE]...')), | |
2608 | "copy|cp": |
|
2634 | "copy|cp": | |
2609 | (copy, |
|
2635 | (copy, | |
2610 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2636 | [('A', 'after', None, _('record a copy that has already occurred')), | |
2611 | ('X', 'exclude', [], _('exclude names matching the given patterns')), |
|
|||
2612 | ('A', 'after', None, _('record a copy that has already occurred')), |
|
|||
2613 | ('f', 'force', None, |
|
2637 | ('f', 'force', None, | |
2614 |
_('forcibly copy over an existing managed file')) |
|
2638 | _('forcibly copy over an existing managed file')), | |
|
2639 | ('I', 'include', [], _('include names matching the given patterns')), | |||
|
2640 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |||
2615 | _('hg copy [OPTION]... [SOURCE]... DEST')), |
|
2641 | _('hg copy [OPTION]... [SOURCE]... DEST')), | |
2616 | "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')), |
|
2642 | "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')), | |
|
2643 | "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')), | |||
2617 | "debugrebuildstate": |
|
2644 | "debugrebuildstate": | |
2618 | (debugrebuildstate, |
|
2645 | (debugrebuildstate, | |
2619 |
[('r', 'rev', |
|
2646 | [('r', 'rev', '', _('revision to rebuild to'))], | |
2620 | _('debugrebuildstate [-r REV] [REV]')), |
|
2647 | _('debugrebuildstate [-r REV] [REV]')), | |
2621 | "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')), |
|
2648 | "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')), | |
2622 | "debugconfig": (debugconfig, [], _('debugconfig')), |
|
2649 | "debugconfig": (debugconfig, [], _('debugconfig')), | |
@@ -2635,20 +2662,19 b' table = {' | |||||
2635 | (diff, |
|
2662 | (diff, | |
2636 | [('r', 'rev', [], _('revision')), |
|
2663 | [('r', 'rev', [], _('revision')), | |
2637 | ('a', 'text', None, _('treat all files as text')), |
|
2664 | ('a', 'text', None, _('treat all files as text')), | |
2638 | ('I', 'include', [], _('include names matching the given patterns')), |
|
|||
2639 | ('p', 'show-function', None, |
|
2665 | ('p', 'show-function', None, | |
2640 | _('show which function each change is in')), |
|
2666 | _('show which function each change is in')), | |
2641 | ('w', 'ignore-all-space', None, |
|
2667 | ('w', 'ignore-all-space', None, | |
2642 | _('ignore white space when comparing lines')), |
|
2668 | _('ignore white space when comparing lines')), | |
2643 | ('X', 'exclude', [], |
|
2669 | ('I', 'include', [], _('include names matching the given patterns')), | |
2644 | _('exclude names matching the given patterns'))], |
|
2670 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |
2645 | _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')), |
|
2671 | _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')), | |
2646 | "^export": |
|
2672 | "^export": | |
2647 | (export, |
|
2673 | (export, | |
2648 | [('o', 'output', '', _('print output to file with formatted name')), |
|
2674 | [('o', 'output', '', _('print output to file with formatted name')), | |
2649 | ('a', 'text', None, _('treat all files as text')), |
|
2675 | ('a', 'text', None, _('treat all files as text')), | |
2650 | ('', 'switch-parent', None, _('diff against the second parent'))], |
|
2676 | ('', 'switch-parent', None, _('diff against the second parent'))], | |
2651 | _('hg export [-a] [-o OUTFILE] REV...')), |
|
2677 | _('hg export [-a] [-o OUTFILESPEC] REV...')), | |
2652 | "forget": |
|
2678 | "forget": | |
2653 | (forget, |
|
2679 | (forget, | |
2654 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2680 | [('I', 'include', [], _('include names matching the given patterns')), | |
@@ -2657,15 +2683,15 b' table = {' | |||||
2657 | "grep": |
|
2683 | "grep": | |
2658 | (grep, |
|
2684 | (grep, | |
2659 | [('0', 'print0', None, _('end fields with NUL')), |
|
2685 | [('0', 'print0', None, _('end fields with NUL')), | |
2660 | ('I', 'include', [], _('include names matching the given patterns')), |
|
|||
2661 | ('X', 'exclude', [], _('exclude names matching the given patterns')), |
|
|||
2662 | ('', 'all', None, _('print all revisions that match')), |
|
2686 | ('', 'all', None, _('print all revisions that match')), | |
2663 | ('i', 'ignore-case', None, _('ignore case when matching')), |
|
2687 | ('i', 'ignore-case', None, _('ignore case when matching')), | |
2664 | ('l', 'files-with-matches', None, |
|
2688 | ('l', 'files-with-matches', None, | |
2665 | _('print only filenames and revs that match')), |
|
2689 | _('print only filenames and revs that match')), | |
2666 | ('n', 'line-number', None, _('print matching line numbers')), |
|
2690 | ('n', 'line-number', None, _('print matching line numbers')), | |
2667 | ('r', 'rev', [], _('search in given revision range')), |
|
2691 | ('r', 'rev', [], _('search in given revision range')), | |
2668 |
('u', 'user', None, _('print user who committed change')) |
|
2692 | ('u', 'user', None, _('print user who committed change')), | |
|
2693 | ('I', 'include', [], _('include names matching the given patterns')), | |||
|
2694 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |||
2669 | _('hg grep [OPTION]... PATTERN [FILE]...')), |
|
2695 | _('hg grep [OPTION]... PATTERN [FILE]...')), | |
2670 | "heads": |
|
2696 | "heads": | |
2671 | (heads, |
|
2697 | (heads, | |
@@ -2681,10 +2707,10 b' table = {' | |||||
2681 | [('p', 'strip', 1, |
|
2707 | [('p', 'strip', 1, | |
2682 | _('directory strip option for patch. This has the same\n') + |
|
2708 | _('directory strip option for patch. This has the same\n') + | |
2683 | _('meaning as the corresponding patch option')), |
|
2709 | _('meaning as the corresponding patch option')), | |
|
2710 | ('b', 'base', '', _('base path')), | |||
2684 | ('f', 'force', None, |
|
2711 | ('f', 'force', None, | |
2685 | _('skip check for outstanding uncommitted changes')), |
|
2712 | _('skip check for outstanding uncommitted changes'))], | |
2686 | ('b', 'base', '', _('base path'))], |
|
2713 | _('hg import [-p NUM] [-b BASE] [-f] PATCH...')), | |
2687 | _('hg import [-f] [-p NUM] [-b BASE] PATCH...')), |
|
|||
2688 | "incoming|in": (incoming, |
|
2714 | "incoming|in": (incoming, | |
2689 | [('M', 'no-merges', None, _('do not show merges')), |
|
2715 | [('M', 'no-merges', None, _('do not show merges')), | |
2690 | ('', 'style', '', _('display using template map file')), |
|
2716 | ('', 'style', '', _('display using template map file')), | |
@@ -2705,9 +2731,7 b' table = {' | |||||
2705 | _('hg locate [OPTION]... [PATTERN]...')), |
|
2731 | _('hg locate [OPTION]... [PATTERN]...')), | |
2706 | "^log|history": |
|
2732 | "^log|history": | |
2707 | (log, |
|
2733 | (log, | |
2708 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2734 | [('b', 'branches', None, _('show branches')), | |
2709 | ('X', 'exclude', [], _('exclude names matching the given patterns')), |
|
|||
2710 | ('b', 'branches', None, _('show branches')), |
|
|||
2711 | ('k', 'keyword', [], _('search for a keyword')), |
|
2735 | ('k', 'keyword', [], _('search for a keyword')), | |
2712 | ('l', 'limit', '', _('limit number of changes displayed')), |
|
2736 | ('l', 'limit', '', _('limit number of changes displayed')), | |
2713 | ('r', 'rev', [], _('show the specified revision or range')), |
|
2737 | ('r', 'rev', [], _('show the specified revision or range')), | |
@@ -2715,8 +2739,10 b' table = {' | |||||
2715 | ('', 'style', '', _('display using template map file')), |
|
2739 | ('', 'style', '', _('display using template map file')), | |
2716 | ('m', 'only-merges', None, _('show only merges')), |
|
2740 | ('m', 'only-merges', None, _('show only merges')), | |
2717 | ('p', 'patch', None, _('show patch')), |
|
2741 | ('p', 'patch', None, _('show patch')), | |
2718 |
('', 'template', '', _('display with template')) |
|
2742 | ('', 'template', '', _('display with template')), | |
2719 | _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')), |
|
2743 | ('I', 'include', [], _('include names matching the given patterns')), | |
|
2744 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |||
|
2745 | _('hg log [OPTION]... [FILE]')), | |||
2720 | "manifest": (manifest, [], _('hg manifest [REV]')), |
|
2746 | "manifest": (manifest, [], _('hg manifest [REV]')), | |
2721 | "outgoing|out": (outgoing, |
|
2747 | "outgoing|out": (outgoing, | |
2722 | [('M', 'no-merges', None, _('do not show merges')), |
|
2748 | [('M', 'no-merges', None, _('do not show merges')), | |
@@ -2724,7 +2750,7 b' table = {' | |||||
2724 | ('', 'style', '', _('display using template map file')), |
|
2750 | ('', 'style', '', _('display using template map file')), | |
2725 | ('n', 'newest-first', None, _('show newest record first')), |
|
2751 | ('n', 'newest-first', None, _('show newest record first')), | |
2726 | ('', 'template', '', _('display with template'))], |
|
2752 | ('', 'template', '', _('display with template'))], | |
2727 |
_('hg outgoing [-p] [-n |
|
2753 | _('hg outgoing [-M] [-p] [-n] [DEST]')), | |
2728 | "^parents": |
|
2754 | "^parents": | |
2729 | (parents, |
|
2755 | (parents, | |
2730 | [('b', 'branches', None, _('show branches')), |
|
2756 | [('b', 'branches', None, _('show branches')), | |
@@ -2740,7 +2766,7 b' table = {' | |||||
2740 | ('r', 'rev', [], _('a specific revision you would like to pull')), |
|
2766 | ('r', 'rev', [], _('a specific revision you would like to pull')), | |
2741 | ('', 'remotecmd', '', |
|
2767 | ('', 'remotecmd', '', | |
2742 | _('specify hg command to run on the remote side'))], |
|
2768 | _('specify hg command to run on the remote side'))], | |
2743 |
_('hg pull [-u] [-e FILE] [-r |
|
2769 | _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')), | |
2744 | "^push": |
|
2770 | "^push": | |
2745 | (push, |
|
2771 | (push, | |
2746 | [('f', 'force', None, _('force push')), |
|
2772 | [('f', 'force', None, _('force push')), | |
@@ -2748,8 +2774,8 b' table = {' | |||||
2748 | ('r', 'rev', [], _('a specific revision you would like to push')), |
|
2774 | ('r', 'rev', [], _('a specific revision you would like to push')), | |
2749 | ('', 'remotecmd', '', |
|
2775 | ('', 'remotecmd', '', | |
2750 | _('specify hg command to run on the remote side'))], |
|
2776 | _('specify hg command to run on the remote side'))], | |
2751 |
_('hg push [-f] [-e FILE] [-r |
|
2777 | _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')), | |
2752 | "rawcommit": |
|
2778 | "debugrawcommit|rawcommit": | |
2753 | (rawcommit, |
|
2779 | (rawcommit, | |
2754 | [('p', 'parent', [], _('parent')), |
|
2780 | [('p', 'parent', [], _('parent')), | |
2755 | ('d', 'date', '', _('date code')), |
|
2781 | ('d', 'date', '', _('date code')), | |
@@ -2757,27 +2783,28 b' table = {' | |||||
2757 | ('F', 'files', '', _('file list')), |
|
2783 | ('F', 'files', '', _('file list')), | |
2758 | ('m', 'message', '', _('commit message')), |
|
2784 | ('m', 'message', '', _('commit message')), | |
2759 | ('l', 'logfile', '', _('commit message file'))], |
|
2785 | ('l', 'logfile', '', _('commit message file'))], | |
2760 | _('hg rawcommit [OPTION]... [FILE]...')), |
|
2786 | _('hg debugrawcommit [OPTION]... [FILE]...')), | |
2761 | "recover": (recover, [], _('hg recover')), |
|
2787 | "recover": (recover, [], _('hg recover')), | |
2762 | "^remove|rm": |
|
2788 | "^remove|rm": | |
2763 | (remove, |
|
2789 | (remove, | |
2764 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2790 | [('f', 'force', None, _('remove file even if modified')), | |
|
2791 | ('I', 'include', [], _('include names matching the given patterns')), | |||
2765 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], |
|
2792 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |
2766 | _('hg remove [OPTION]... FILE...')), |
|
2793 | _('hg remove [OPTION]... FILE...')), | |
2767 | "rename|mv": |
|
2794 | "rename|mv": | |
2768 | (rename, |
|
2795 | (rename, | |
2769 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2796 | [('A', 'after', None, _('record a rename that has already occurred')), | |
2770 | ('X', 'exclude', [], _('exclude names matching the given patterns')), |
|
|||
2771 | ('A', 'after', None, _('record a rename that has already occurred')), |
|
|||
2772 | ('f', 'force', None, |
|
2797 | ('f', 'force', None, | |
2773 |
_('forcibly copy over an existing managed file')) |
|
2798 | _('forcibly copy over an existing managed file')), | |
2774 | _('hg rename [OPTION]... [SOURCE]... DEST')), |
|
2799 | ('I', 'include', [], _('include names matching the given patterns')), | |
|
2800 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |||
|
2801 | _('hg rename [OPTION]... SOURCE... DEST')), | |||
2775 | "^revert": |
|
2802 | "^revert": | |
2776 | (revert, |
|
2803 | (revert, | |
2777 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2804 | [('r', 'rev', '', _('revision to revert to')), | |
2778 |
(' |
|
2805 | ('I', 'include', [], _('include names matching the given patterns')), | |
2779 | ('r', 'rev', '', _('revision to revert to'))], |
|
2806 | ('X', 'exclude', [], _('exclude names matching the given patterns'))], | |
2780 |
_('hg revert [- |
|
2807 | _('hg revert [-r REV] [NAME]...')), | |
2781 | "root": (root, [], _('hg root')), |
|
2808 | "root": (root, [], _('hg root')), | |
2782 | "^serve": |
|
2809 | "^serve": | |
2783 | (serve, |
|
2810 | (serve, | |
@@ -2791,7 +2818,7 b' table = {' | |||||
2791 | _('name to show in web pages (default: working dir)')), |
|
2818 | _('name to show in web pages (default: working dir)')), | |
2792 | ('', 'pid-file', '', _('name of file to write process ID to')), |
|
2819 | ('', 'pid-file', '', _('name of file to write process ID to')), | |
2793 | ('', 'stdio', None, _('for remote clients')), |
|
2820 | ('', 'stdio', None, _('for remote clients')), | |
2794 | ('', 'templates', '', _('web templates to use')), |
|
2821 | ('t', 'templates', '', _('web templates to use')), | |
2795 | ('', 'style', '', _('template style to use')), |
|
2822 | ('', 'style', '', _('template style to use')), | |
2796 | ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))], |
|
2823 | ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))], | |
2797 | _('hg serve [OPTION]...')), |
|
2824 | _('hg serve [OPTION]...')), | |
@@ -2815,7 +2842,7 b' table = {' | |||||
2815 | ('d', 'date', '', _('record datecode as commit date')), |
|
2842 | ('d', 'date', '', _('record datecode as commit date')), | |
2816 | ('u', 'user', '', _('record user as commiter')), |
|
2843 | ('u', 'user', '', _('record user as commiter')), | |
2817 | ('r', 'rev', '', _('revision to tag'))], |
|
2844 | ('r', 'rev', '', _('revision to tag'))], | |
2818 |
_('hg tag [- |
|
2845 | _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')), | |
2819 | "tags": (tags, [], _('hg tags')), |
|
2846 | "tags": (tags, [], _('hg tags')), | |
2820 | "tip": |
|
2847 | "tip": | |
2821 | (tip, |
|
2848 | (tip, | |
@@ -2823,7 +2850,7 b' table = {' | |||||
2823 | ('', 'style', '', _('display using template map file')), |
|
2850 | ('', 'style', '', _('display using template map file')), | |
2824 | ('p', 'patch', None, _('show patch')), |
|
2851 | ('p', 'patch', None, _('show patch')), | |
2825 | ('', 'template', '', _('display with template'))], |
|
2852 | ('', 'template', '', _('display with template'))], | |
2826 |
_('hg [-b] [-p] |
|
2853 | _('hg tip [-b] [-p]')), | |
2827 | "unbundle": |
|
2854 | "unbundle": | |
2828 | (unbundle, |
|
2855 | (unbundle, | |
2829 | [('u', 'update', None, |
|
2856 | [('u', 'update', None, | |
@@ -2844,7 +2871,8 b' table = {' | |||||
2844 | } |
|
2871 | } | |
2845 |
|
2872 | |||
2846 | globalopts = [ |
|
2873 | globalopts = [ | |
2847 |
('R', 'repository', '', |
|
2874 | ('R', 'repository', '', | |
|
2875 | _('repository root directory or symbolic path name')), | |||
2848 | ('', 'cwd', '', _('change working directory')), |
|
2876 | ('', 'cwd', '', _('change working directory')), | |
2849 | ('y', 'noninteractive', None, |
|
2877 | ('y', 'noninteractive', None, | |
2850 | _('do not prompt, assume \'yes\' for any required answers')), |
|
2878 | _('do not prompt, assume \'yes\' for any required answers')), | |
@@ -2859,28 +2887,49 b' globalopts = [' | |||||
2859 | ('h', 'help', None, _('display help and exit')), |
|
2887 | ('h', 'help', None, _('display help and exit')), | |
2860 | ] |
|
2888 | ] | |
2861 |
|
2889 | |||
2862 |
norepo = ("clone init version help debugancestor debugco |
|
2890 | norepo = ("clone init version help debugancestor debugcomplete debugdata" | |
2863 |
" debugindex debugindexdot |
|
2891 | " debugindex debugindexdot") | |
2864 |
|
2892 | optionalrepo = ("paths debugconfig") | ||
2865 | def find(cmd): |
|
2893 | ||
2866 | """Return (aliases, command table entry) for command string.""" |
|
2894 | def findpossible(cmd): | |
2867 | choice = None |
|
2895 | """ | |
2868 | count = 0 |
|
2896 | Return cmd -> (aliases, command table entry) | |
|
2897 | for each matching command | |||
|
2898 | """ | |||
|
2899 | choice = {} | |||
|
2900 | debugchoice = {} | |||
2869 | for e in table.keys(): |
|
2901 | for e in table.keys(): | |
2870 | aliases = e.lstrip("^").split("|") |
|
2902 | aliases = e.lstrip("^").split("|") | |
2871 | if cmd in aliases: |
|
2903 | if cmd in aliases: | |
2872 |
|
|
2904 | choice[cmd] = (aliases, table[e]) | |
|
2905 | continue | |||
2873 | for a in aliases: |
|
2906 | for a in aliases: | |
2874 | if a.startswith(cmd): |
|
2907 | if a.startswith(cmd): | |
2875 | count += 1 |
|
2908 | if aliases[0].startswith("debug"): | |
2876 | choice = aliases, table[e] |
|
2909 | debugchoice[a] = (aliases, table[e]) | |
|
2910 | else: | |||
|
2911 | choice[a] = (aliases, table[e]) | |||
2877 | break |
|
2912 | break | |
2878 |
|
2913 | |||
2879 | if count > 1: |
|
2914 | if not choice and debugchoice: | |
2880 | raise AmbiguousCommand(cmd) |
|
2915 | choice = debugchoice | |
|
2916 | ||||
|
2917 | return choice | |||
|
2918 | ||||
|
2919 | def find(cmd): | |||
|
2920 | """Return (aliases, command table entry) for command string.""" | |||
|
2921 | choice = findpossible(cmd) | |||
|
2922 | ||||
|
2923 | if choice.has_key(cmd): | |||
|
2924 | return choice[cmd] | |||
|
2925 | ||||
|
2926 | if len(choice) > 1: | |||
|
2927 | clist = choice.keys() | |||
|
2928 | clist.sort() | |||
|
2929 | raise AmbiguousCommand(cmd, clist) | |||
2881 |
|
2930 | |||
2882 | if choice: |
|
2931 | if choice: | |
2883 | return choice |
|
2932 | return choice.values()[0] | |
2884 |
|
2933 | |||
2885 | raise UnknownCommand(cmd) |
|
2934 | raise UnknownCommand(cmd) | |
2886 |
|
2935 | |||
@@ -2968,6 +3017,9 b' def dispatch(args):' | |||||
2968 | mod = getattr(mod, comp) |
|
3017 | mod = getattr(mod, comp) | |
2969 | return mod |
|
3018 | return mod | |
2970 | try: |
|
3019 | try: | |
|
3020 | try: | |||
|
3021 | mod = importh("hgext." + x[0]) | |||
|
3022 | except ImportError: | |||
2971 | mod = importh(x[0]) |
|
3023 | mod = importh(x[0]) | |
2972 | except Exception, inst: |
|
3024 | except Exception, inst: | |
2973 | on_exception(Exception, inst) |
|
3025 | on_exception(Exception, inst) | |
@@ -2983,22 +3035,6 b' def dispatch(args):' | |||||
2983 |
|
3035 | |||
2984 | try: |
|
3036 | try: | |
2985 | cmd, func, args, options, cmdoptions = parse(u, args) |
|
3037 | cmd, func, args, options, cmdoptions = parse(u, args) | |
2986 | except ParseError, inst: |
|
|||
2987 | if inst.args[0]: |
|
|||
2988 | u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1])) |
|
|||
2989 | help_(u, inst.args[0]) |
|
|||
2990 | else: |
|
|||
2991 | u.warn(_("hg: %s\n") % inst.args[1]) |
|
|||
2992 | help_(u, 'shortlist') |
|
|||
2993 | sys.exit(-1) |
|
|||
2994 | except AmbiguousCommand, inst: |
|
|||
2995 | u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0]) |
|
|||
2996 | sys.exit(1) |
|
|||
2997 | except UnknownCommand, inst: |
|
|||
2998 | u.warn(_("hg: unknown command '%s'\n") % inst.args[0]) |
|
|||
2999 | help_(u, 'shortlist') |
|
|||
3000 | sys.exit(1) |
|
|||
3001 |
|
||||
3002 | if options["time"]: |
|
3038 | if options["time"]: | |
3003 | def get_times(): |
|
3039 | def get_times(): | |
3004 | t = os.times() |
|
3040 | t = os.times() | |
@@ -3020,7 +3056,16 b' def dispatch(args):' | |||||
3020 | pdb.set_trace() |
|
3056 | pdb.set_trace() | |
3021 |
|
3057 | |||
3022 | try: |
|
3058 | try: | |
|
3059 | if options['cwd']: | |||
3023 | try: |
|
3060 | try: | |
|
3061 | os.chdir(options['cwd']) | |||
|
3062 | except OSError, inst: | |||
|
3063 | raise util.Abort('%s: %s' % | |||
|
3064 | (options['cwd'], inst.strerror)) | |||
|
3065 | ||||
|
3066 | path = u.expandpath(options["repository"]) or "" | |||
|
3067 | repo = path and hg.repository(u, path=path) or None | |||
|
3068 | ||||
3024 | if options['help']: |
|
3069 | if options['help']: | |
3025 | help_(u, cmd, options['version']) |
|
3070 | help_(u, cmd, options['version']) | |
3026 | sys.exit(0) |
|
3071 | sys.exit(0) | |
@@ -3031,35 +3076,45 b' def dispatch(args):' | |||||
3031 | help_(u, 'shortlist') |
|
3076 | help_(u, 'shortlist') | |
3032 | sys.exit(0) |
|
3077 | sys.exit(0) | |
3033 |
|
3078 | |||
3034 | if options['cwd']: |
|
3079 | if cmd not in norepo.split(): | |
3035 | try: |
|
3080 | try: | |
3036 |
|
|
3081 | if not repo: | |
3037 | except OSError, inst: |
|
3082 | repo = hg.repository(u, path=path) | |
3038 | raise util.Abort('%s: %s' % |
|
3083 | u = repo.ui | |
3039 | (options['cwd'], inst.strerror)) |
|
|||
3040 |
|
||||
3041 | if cmd not in norepo.split(): |
|
|||
3042 | path = options["repository"] or "" |
|
|||
3043 | repo = hg.repository(ui=u, path=path) |
|
|||
3044 | for x in external: |
|
3084 | for x in external: | |
3045 | if hasattr(x, 'reposetup'): |
|
3085 | if hasattr(x, 'reposetup'): | |
3046 | x.reposetup(u, repo) |
|
3086 | x.reposetup(u, repo) | |
|
3087 | except hg.RepoError: | |||
|
3088 | if cmd not in optionalrepo.split(): | |||
|
3089 | raise | |||
3047 | d = lambda: func(u, repo, *args, **cmdoptions) |
|
3090 | d = lambda: func(u, repo, *args, **cmdoptions) | |
3048 | else: |
|
3091 | else: | |
3049 | d = lambda: func(u, *args, **cmdoptions) |
|
3092 | d = lambda: func(u, *args, **cmdoptions) | |
3050 |
|
3093 | |||
|
3094 | try: | |||
3051 | if options['profile']: |
|
3095 | if options['profile']: | |
3052 | import hotshot, hotshot.stats |
|
3096 | import hotshot, hotshot.stats | |
3053 | prof = hotshot.Profile("hg.prof") |
|
3097 | prof = hotshot.Profile("hg.prof") | |
3054 | r = prof.runcall(d) |
|
3098 | try: | |
|
3099 | try: | |||
|
3100 | return prof.runcall(d) | |||
|
3101 | except: | |||
|
3102 | try: | |||
|
3103 | u.warn(_('exception raised - generating ' | |||
|
3104 | 'profile anyway\n')) | |||
|
3105 | except: | |||
|
3106 | pass | |||
|
3107 | raise | |||
|
3108 | finally: | |||
3055 | prof.close() |
|
3109 | prof.close() | |
3056 | stats = hotshot.stats.load("hg.prof") |
|
3110 | stats = hotshot.stats.load("hg.prof") | |
3057 | stats.strip_dirs() |
|
3111 | stats.strip_dirs() | |
3058 | stats.sort_stats('time', 'calls') |
|
3112 | stats.sort_stats('time', 'calls') | |
3059 | stats.print_stats(40) |
|
3113 | stats.print_stats(40) | |
3060 | return r |
|
|||
3061 | else: |
|
3114 | else: | |
3062 | return d() |
|
3115 | return d() | |
|
3116 | finally: | |||
|
3117 | u.flush() | |||
3063 | except: |
|
3118 | except: | |
3064 | # enter the debugger when we hit an exception |
|
3119 | # enter the debugger when we hit an exception | |
3065 | if options['debugger']: |
|
3120 | if options['debugger']: | |
@@ -3067,6 +3122,22 b' def dispatch(args):' | |||||
3067 | if options['traceback']: |
|
3122 | if options['traceback']: | |
3068 | traceback.print_exc() |
|
3123 | traceback.print_exc() | |
3069 | raise |
|
3124 | raise | |
|
3125 | except ParseError, inst: | |||
|
3126 | if inst.args[0]: | |||
|
3127 | u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1])) | |||
|
3128 | help_(u, inst.args[0]) | |||
|
3129 | else: | |||
|
3130 | u.warn(_("hg: %s\n") % inst.args[1]) | |||
|
3131 | help_(u, 'shortlist') | |||
|
3132 | sys.exit(-1) | |||
|
3133 | except AmbiguousCommand, inst: | |||
|
3134 | u.warn(_("hg: command '%s' is ambiguous:\n %s\n") % | |||
|
3135 | (inst.args[0], " ".join(inst.args[1]))) | |||
|
3136 | sys.exit(1) | |||
|
3137 | except UnknownCommand, inst: | |||
|
3138 | u.warn(_("hg: unknown command '%s'\n") % inst.args[0]) | |||
|
3139 | help_(u, 'shortlist') | |||
|
3140 | sys.exit(1) | |||
3070 | except hg.RepoError, inst: |
|
3141 | except hg.RepoError, inst: | |
3071 | u.warn(_("abort: "), inst, "!\n") |
|
3142 | u.warn(_("abort: "), inst, "!\n") | |
3072 | except revlog.RevlogError, inst: |
|
3143 | except revlog.RevlogError, inst: | |
@@ -3113,12 +3184,6 b' def dispatch(args):' | |||||
3113 | u.debug(inst, "\n") |
|
3184 | u.debug(inst, "\n") | |
3114 | u.warn(_("%s: invalid arguments\n") % cmd) |
|
3185 | u.warn(_("%s: invalid arguments\n") % cmd) | |
3115 | help_(u, cmd) |
|
3186 | help_(u, cmd) | |
3116 | except AmbiguousCommand, inst: |
|
|||
3117 | u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0]) |
|
|||
3118 | help_(u, 'shortlist') |
|
|||
3119 | except UnknownCommand, inst: |
|
|||
3120 | u.warn(_("hg: unknown command '%s'\n") % inst.args[0]) |
|
|||
3121 | help_(u, 'shortlist') |
|
|||
3122 | except SystemExit: |
|
3187 | except SystemExit: | |
3123 | # don't catch this in the catch-all below |
|
3188 | # don't catch this in the catch-all below | |
3124 | raise |
|
3189 | raise |
@@ -1,15 +1,125 b'' | |||||
1 | def demandload(scope, modules): |
|
1 | '''Demand load modules when used, not when imported.''' | |
2 | class d: |
|
2 | ||
3 | def __getattr__(self, name): |
|
3 | __author__ = '''Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>. | |
4 | mod = self.__dict__["mod"] |
|
4 | This software may be used and distributed according to the terms | |
5 | scope = self.__dict__["scope"] |
|
5 | of the GNU General Public License, incorporated herein by reference.''' | |
6 | scope[mod] = __import__(mod, scope, scope, []) |
|
6 | ||
7 | return getattr(scope[mod], name) |
|
7 | # this is based on matt's original demandload module. it is a | |
|
8 | # complete rewrite. some time, we may need to support syntax of | |||
|
9 | # "import foo as bar". | |||
|
10 | ||||
|
11 | class _importer(object): | |||
|
12 | '''import a module. it is not imported until needed, and is | |||
|
13 | imported at most once per scope.''' | |||
|
14 | ||||
|
15 | def __init__(self, scope, modname, fromlist): | |||
|
16 | '''scope is context (globals() or locals()) in which import | |||
|
17 | should be made. modname is name of module to import. | |||
|
18 | fromlist is list of modules for "from foo import ..." | |||
|
19 | emulation.''' | |||
|
20 | ||||
|
21 | self.scope = scope | |||
|
22 | self.modname = modname | |||
|
23 | self.fromlist = fromlist | |||
|
24 | self.mod = None | |||
|
25 | ||||
|
26 | def module(self): | |||
|
27 | '''import the module if needed, and return.''' | |||
|
28 | if self.mod is None: | |||
|
29 | self.mod = __import__(self.modname, self.scope, self.scope, | |||
|
30 | self.fromlist) | |||
|
31 | del self.modname, self.fromlist | |||
|
32 | return self.mod | |||
|
33 | ||||
|
34 | class _replacer(object): | |||
|
35 | '''placeholder for a demand loaded module. demandload puts this in | |||
|
36 | a target scope. when an attribute of this object is looked up, | |||
|
37 | this object is replaced in the target scope with the actual | |||
|
38 | module. | |||
|
39 | ||||
|
40 | we use __getattribute__ to avoid namespace clashes between | |||
|
41 | placeholder object and real module.''' | |||
|
42 | ||||
|
43 | def __init__(self, importer, target): | |||
|
44 | self.importer = importer | |||
|
45 | self.target = target | |||
|
46 | # consider case where we do this: | |||
|
47 | # demandload(globals(), 'foo.bar foo.quux') | |||
|
48 | # foo will already exist in target scope when we get to | |||
|
49 | # foo.quux. so we remember that we will need to demandload | |||
|
50 | # quux into foo's scope when we really load it. | |||
|
51 | self.later = [] | |||
|
52 | ||||
|
53 | def module(self): | |||
|
54 | return object.__getattribute__(self, 'importer').module() | |||
|
55 | ||||
|
56 | def __getattribute__(self, key): | |||
|
57 | '''look up an attribute in a module and return it. replace the | |||
|
58 | name of the module in the caller\'s dict with the actual | |||
|
59 | module.''' | |||
8 |
|
60 | |||
9 | for m in modules.split(): |
|
61 | module = object.__getattribute__(self, 'module')() | |
10 | dl = d() |
|
62 | target = object.__getattribute__(self, 'target') | |
11 | dl.mod = m |
|
63 | importer = object.__getattribute__(self, 'importer') | |
12 | dl.scope = scope |
|
64 | later = object.__getattribute__(self, 'later') | |
13 | scope[m] = dl |
|
65 | ||
|
66 | if later: | |||
|
67 | demandload(module.__dict__, ' '.join(later)) | |||
|
68 | ||||
|
69 | importer.scope[target] = module | |||
|
70 | ||||
|
71 | return getattr(module, key) | |||
|
72 | ||||
|
73 | class _replacer_from(_replacer): | |||
|
74 | '''placeholder for a demand loaded module. used for "from foo | |||
|
75 | import ..." emulation. semantics of this are different than | |||
|
76 | regular import, so different implementation needed.''' | |||
|
77 | ||||
|
78 | def module(self): | |||
|
79 | importer = object.__getattribute__(self, 'importer') | |||
|
80 | target = object.__getattribute__(self, 'target') | |||
|
81 | ||||
|
82 | return getattr(importer.module(), target) | |||
|
83 | ||||
|
84 | def demandload(scope, modules): | |||
|
85 | '''import modules into scope when each is first used. | |||
|
86 | ||||
|
87 | scope should be the value of globals() in the module calling this | |||
|
88 | function, or locals() in the calling function. | |||
|
89 | ||||
|
90 | modules is a string listing module names, separated by white | |||
|
91 | space. names are handled like this: | |||
14 |
|
|
92 | ||
|
93 | foo import foo | |||
|
94 | foo bar import foo, bar | |||
|
95 | foo.bar import foo.bar | |||
|
96 | foo:bar from foo import bar | |||
|
97 | foo:bar,quux from foo import bar, quux | |||
|
98 | foo.bar:quux from foo.bar import quux''' | |||
15 |
|
99 | |||
|
100 | for mod in modules.split(): | |||
|
101 | col = mod.find(':') | |||
|
102 | if col >= 0: | |||
|
103 | fromlist = mod[col+1:].split(',') | |||
|
104 | mod = mod[:col] | |||
|
105 | else: | |||
|
106 | fromlist = [] | |||
|
107 | importer = _importer(scope, mod, fromlist) | |||
|
108 | if fromlist: | |||
|
109 | for name in fromlist: | |||
|
110 | scope[name] = _replacer_from(importer, name) | |||
|
111 | else: | |||
|
112 | dot = mod.find('.') | |||
|
113 | if dot >= 0: | |||
|
114 | basemod = mod[:dot] | |||
|
115 | val = scope.get(basemod) | |||
|
116 | # if base module has already been demandload()ed, | |||
|
117 | # remember to load this submodule into its namespace | |||
|
118 | # when needed. | |||
|
119 | if isinstance(val, _replacer): | |||
|
120 | later = object.__getattribute__(val, 'later') | |||
|
121 | later.append(mod[dot+1:]) | |||
|
122 | continue | |||
|
123 | else: | |||
|
124 | basemod = mod | |||
|
125 | scope[basemod] = _replacer(importer, basemod) |
@@ -34,30 +34,27 b' def get_mtime(repo_path):' | |||||
34 | return os.stat(hg_path).st_mtime |
|
34 | return os.stat(hg_path).st_mtime | |
35 |
|
35 | |||
36 | def staticfile(directory, fname): |
|
36 | def staticfile(directory, fname): | |
37 | fname = os.path.realpath(os.path.join(directory, fname)) |
|
37 | """return a file inside directory with guessed content-type header | |
|
38 | ||||
|
39 | fname always uses '/' as directory separator and isn't allowed to | |||
|
40 | contain unusual path components. | |||
|
41 | Content-type is guessed using the mimetypes module. | |||
|
42 | Return an empty string if fname is illegal or file not found. | |||
38 |
|
|
43 | ||
|
44 | """ | |||
|
45 | parts = fname.split('/') | |||
|
46 | path = directory | |||
|
47 | for part in parts: | |||
|
48 | if (part in ('', os.curdir, os.pardir) or | |||
|
49 | os.sep in part or os.altsep is not None and os.altsep in part): | |||
|
50 | return "" | |||
|
51 | path = os.path.join(path, part) | |||
39 | try: |
|
52 | try: | |
40 | # the static dir should be a substring in the real |
|
53 | os.stat(path) | |
41 | # file path, if it is not, we have something strange |
|
54 | ct = mimetypes.guess_type(path)[0] or "text/plain" | |
42 | # going on => security breach attempt? |
|
55 | return "Content-type: %s\n\n%s" % (ct, file(path).read()) | |
43 | # |
|
56 | except (TypeError, OSError): | |
44 | # This will either: |
|
57 | # illegal fname or unreadable file | |
45 | # 1) find the `static' path at index 0 = success |
|
|||
46 | # 2) find the `static' path at other index = error |
|
|||
47 | # 3) not find the `static' path = ValueError generated |
|
|||
48 | if fname.index(directory) != 0: |
|
|||
49 | # generate ValueError manually |
|
|||
50 | raise ValueError() |
|
|||
51 |
|
||||
52 | os.stat(fname) |
|
|||
53 |
|
||||
54 | ct = mimetypes.guess_type(fname)[0] or "text/plain" |
|
|||
55 | return "Content-type: %s\n\n%s" % (ct, file(fname).read()) |
|
|||
56 | except ValueError: |
|
|||
57 | # security breach attempt |
|
|||
58 | return "" |
|
|||
59 | except OSError, e: |
|
|||
60 | if e.errno == errno.ENOENT: |
|
|||
61 |
|
|
58 | return "" | |
62 |
|
59 | |||
63 | class hgrequest(object): |
|
60 | class hgrequest(object): | |
@@ -739,7 +736,7 b' class hgweb(object):' | |||||
739 |
|
736 | |||
740 | def run(self, req=hgrequest()): |
|
737 | def run(self, req=hgrequest()): | |
741 | def clean(path): |
|
738 | def clean(path): | |
742 |
p = |
|
739 | p = util.normpath(path) | |
743 | if p[:2] == "..": |
|
740 | if p[:2] == "..": | |
744 | raise "suspicious path" |
|
741 | raise "suspicious path" | |
745 | return p |
|
742 | return p | |
@@ -1001,17 +998,27 b' def create_server(repo):' | |||||
1001 | class hgwebdir(object): |
|
998 | class hgwebdir(object): | |
1002 | def __init__(self, config): |
|
999 | def __init__(self, config): | |
1003 | def cleannames(items): |
|
1000 | def cleannames(items): | |
1004 |
return [(name.strip( |
|
1001 | return [(name.strip(os.sep), path) for name, path in items] | |
1005 |
|
1002 | |||
1006 |
if |
|
1003 | if isinstance(config, (list, tuple)): | |
1007 | self.repos = cleannames(config) |
|
1004 | self.repos = cleannames(config) | |
1008 |
elif |
|
1005 | elif isinstance(config, dict): | |
1009 | self.repos = cleannames(config.items()) |
|
1006 | self.repos = cleannames(config.items()) | |
1010 | self.repos.sort() |
|
1007 | self.repos.sort() | |
1011 | else: |
|
1008 | else: | |
1012 | cp = ConfigParser.SafeConfigParser() |
|
1009 | cp = ConfigParser.SafeConfigParser() | |
1013 | cp.read(config) |
|
1010 | cp.read(config) | |
1014 |
self.repos = |
|
1011 | self.repos = [] | |
|
1012 | if cp.has_section('paths'): | |||
|
1013 | self.repos.extend(cleannames(cp.items('paths'))) | |||
|
1014 | if cp.has_section('collections'): | |||
|
1015 | for prefix, root in cp.items('collections'): | |||
|
1016 | for path in util.walkrepos(root): | |||
|
1017 | repo = os.path.normpath(path) | |||
|
1018 | name = repo | |||
|
1019 | if name.startswith(prefix): | |||
|
1020 | name = name[len(prefix):] | |||
|
1021 | self.repos.append((name.lstrip(os.sep), repo)) | |||
1015 | self.repos.sort() |
|
1022 | self.repos.sort() | |
1016 |
|
1023 | |||
1017 | def run(self, req=hgrequest()): |
|
1024 | def run(self, req=hgrequest()): |
@@ -67,6 +67,9 b' class httprepository(remoterepository):' | |||||
67 | def dev(self): |
|
67 | def dev(self): | |
68 | return -1 |
|
68 | return -1 | |
69 |
|
69 | |||
|
70 | def lock(self): | |||
|
71 | raise util.Abort(_('operation not supported over http')) | |||
|
72 | ||||
70 | def do_cmd(self, cmd, **args): |
|
73 | def do_cmd(self, cmd, **args): | |
71 | self.ui.debug(_("sending %s command\n") % cmd) |
|
74 | self.ui.debug(_("sending %s command\n") % cmd) | |
72 | q = {"cmd": cmd} |
|
75 | q = {"cmd": cmd} |
@@ -10,10 +10,12 b' import filelog, manifest, changelog, dir' | |||||
10 | from node import * |
|
10 | from node import * | |
11 | from i18n import gettext as _ |
|
11 | from i18n import gettext as _ | |
12 | from demandload import * |
|
12 | from demandload import * | |
13 | demandload(globals(), "re lock transaction tempfile stat mdiff errno") |
|
13 | demandload(globals(), "re lock transaction tempfile stat mdiff errno ui") | |
14 |
|
14 | |||
15 | class localrepository(object): |
|
15 | class localrepository(object): | |
16 | def __init__(self, ui, path=None, create=0): |
|
16 | def __del__(self): | |
|
17 | self.transhandle = None | |||
|
18 | def __init__(self, parentui, path=None, create=0): | |||
17 | if not path: |
|
19 | if not path: | |
18 | p = os.getcwd() |
|
20 | p = os.getcwd() | |
19 | while not os.path.isdir(os.path.join(p, ".hg")): |
|
21 | while not os.path.isdir(os.path.join(p, ".hg")): | |
@@ -28,7 +30,7 b' class localrepository(object):' | |||||
28 | raise repo.RepoError(_("repository %s not found") % path) |
|
30 | raise repo.RepoError(_("repository %s not found") % path) | |
29 |
|
31 | |||
30 | self.root = os.path.abspath(path) |
|
32 | self.root = os.path.abspath(path) | |
31 | self.ui = ui |
|
33 | self.ui = ui.ui(parentui=parentui) | |
32 | self.opener = util.opener(self.path) |
|
34 | self.opener = util.opener(self.path) | |
33 | self.wopener = util.opener(self.root) |
|
35 | self.wopener = util.opener(self.root) | |
34 | self.manifest = manifest.manifest(self.opener) |
|
36 | self.manifest = manifest.manifest(self.opener) | |
@@ -37,42 +39,23 b' class localrepository(object):' | |||||
37 | self.nodetagscache = None |
|
39 | self.nodetagscache = None | |
38 | self.encodepats = None |
|
40 | self.encodepats = None | |
39 | self.decodepats = None |
|
41 | self.decodepats = None | |
|
42 | self.transhandle = None | |||
40 |
|
43 | |||
41 | if create: |
|
44 | if create: | |
42 | os.mkdir(self.path) |
|
45 | os.mkdir(self.path) | |
43 | os.mkdir(self.join("data")) |
|
46 | os.mkdir(self.join("data")) | |
44 |
|
47 | |||
45 | self.dirstate = dirstate.dirstate(self.opener, ui, self.root) |
|
48 | self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root) | |
46 | try: |
|
49 | try: | |
47 | self.ui.readconfig(self.join("hgrc")) |
|
50 | self.ui.readconfig(self.join("hgrc"), self.root) | |
48 | except IOError: |
|
51 | except IOError: | |
49 | pass |
|
52 | pass | |
50 |
|
53 | |||
51 | def hook(self, name, throw=False, **args): |
|
54 | def hook(self, name, throw=False, **args): | |
52 | def runhook(name, cmd): |
|
55 | def runhook(name, cmd): | |
53 | self.ui.note(_("running hook %s: %s\n") % (name, cmd)) |
|
56 | self.ui.note(_("running hook %s: %s\n") % (name, cmd)) | |
54 | old = {} |
|
57 | env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()]) | |
55 | for k, v in args.items(): |
|
58 | r = util.system(cmd, environ=env, cwd=self.root) | |
56 | k = k.upper() |
|
|||
57 | old['HG_' + k] = os.environ.get(k, None) |
|
|||
58 | old[k] = os.environ.get(k, None) |
|
|||
59 | os.environ['HG_' + k] = str(v) |
|
|||
60 | os.environ[k] = str(v) |
|
|||
61 |
|
||||
62 | try: |
|
|||
63 | # Hooks run in the repository root |
|
|||
64 | olddir = os.getcwd() |
|
|||
65 | os.chdir(self.root) |
|
|||
66 | r = os.system(cmd) |
|
|||
67 | finally: |
|
|||
68 | for k, v in old.items(): |
|
|||
69 | if v is not None: |
|
|||
70 | os.environ[k] = v |
|
|||
71 | else: |
|
|||
72 | del os.environ[k] |
|
|||
73 |
|
||||
74 | os.chdir(olddir) |
|
|||
75 |
|
||||
76 | if r: |
|
59 | if r: | |
77 | desc, r = util.explain_exit(r) |
|
60 | desc, r = util.explain_exit(r) | |
78 | if throw: |
|
61 | if throw: | |
@@ -82,9 +65,10 b' class localrepository(object):' | |||||
82 | return True |
|
65 | return True | |
83 |
|
66 | |||
84 | r = True |
|
67 | r = True | |
85 |
for hname, cmd in self.ui.configitems("hooks") |
|
68 | hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks") | |
86 |
|
|
69 | if hname.split(".", 1)[0] == name and cmd] | |
87 | if s[0] == name and cmd: |
|
70 | hooks.sort() | |
|
71 | for hname, cmd in hooks: | |||
88 |
|
|
72 | r = runhook(hname, cmd) and r | |
89 | return r |
|
73 | return r | |
90 |
|
74 | |||
@@ -215,6 +199,10 b' class localrepository(object):' | |||||
215 | return self.wopener(filename, 'w').write(data) |
|
199 | return self.wopener(filename, 'w').write(data) | |
216 |
|
200 | |||
217 | def transaction(self): |
|
201 | def transaction(self): | |
|
202 | tr = self.transhandle | |||
|
203 | if tr != None and tr.running(): | |||
|
204 | return tr.nest() | |||
|
205 | ||||
218 | # save dirstate for undo |
|
206 | # save dirstate for undo | |
219 | try: |
|
207 | try: | |
220 | ds = self.opener("dirstate").read() |
|
208 | ds = self.opener("dirstate").read() | |
@@ -222,13 +210,11 b' class localrepository(object):' | |||||
222 | ds = "" |
|
210 | ds = "" | |
223 | self.opener("journal.dirstate", "w").write(ds) |
|
211 | self.opener("journal.dirstate", "w").write(ds) | |
224 |
|
212 | |||
225 | def after(): |
|
213 | tr = transaction.transaction(self.ui.warn, self.opener, | |
226 | util.rename(self.join("journal"), self.join("undo")) |
|
214 | self.join("journal"), | |
227 | util.rename(self.join("journal.dirstate"), |
|
215 | aftertrans(self.path)) | |
228 | self.join("undo.dirstate")) |
|
216 | self.transhandle = tr | |
229 |
|
217 | return tr | ||
230 | return transaction.transaction(self.ui.warn, self.opener, |
|
|||
231 | self.join("journal"), after) |
|
|||
232 |
|
218 | |||
233 | def recover(self): |
|
219 | def recover(self): | |
234 | l = self.lock() |
|
220 | l = self.lock() | |
@@ -366,7 +352,7 b' class localrepository(object):' | |||||
366 | self.dirstate.setparents(n, nullid) |
|
352 | self.dirstate.setparents(n, nullid) | |
367 |
|
353 | |||
368 | def commit(self, files=None, text="", user=None, date=None, |
|
354 | def commit(self, files=None, text="", user=None, date=None, | |
369 | match=util.always, force=False, wlock=None): |
|
355 | match=util.always, force=False, lock=None, wlock=None): | |
370 | commit = [] |
|
356 | commit = [] | |
371 | remove = [] |
|
357 | remove = [] | |
372 | changed = [] |
|
358 | changed = [] | |
@@ -404,7 +390,8 b' class localrepository(object):' | |||||
404 |
|
390 | |||
405 | if not wlock: |
|
391 | if not wlock: | |
406 | wlock = self.wlock() |
|
392 | wlock = self.wlock() | |
407 |
|
|
393 | if not lock: | |
|
394 | lock = self.lock() | |||
408 | tr = self.transaction() |
|
395 | tr = self.transaction() | |
409 |
|
396 | |||
410 | # check in files |
|
397 | # check in files | |
@@ -519,6 +506,12 b' class localrepository(object):' | |||||
519 | del mf[fn] |
|
506 | del mf[fn] | |
520 | return mf |
|
507 | return mf | |
521 |
|
508 | |||
|
509 | if node1: | |||
|
510 | # read the manifest from node1 before the manifest from node2, | |||
|
511 | # so that we'll hit the manifest cache if we're going through | |||
|
512 | # all the revisions in parent->child order. | |||
|
513 | mf1 = mfmatches(node1) | |||
|
514 | ||||
522 | # are we comparing the working directory? |
|
515 | # are we comparing the working directory? | |
523 | if not node2: |
|
516 | if not node2: | |
524 | if not wlock: |
|
517 | if not wlock: | |
@@ -557,8 +550,6 b' class localrepository(object):' | |||||
557 | # flush lists from dirstate before comparing manifests |
|
550 | # flush lists from dirstate before comparing manifests | |
558 | modified, added = [], [] |
|
551 | modified, added = [], [] | |
559 |
|
552 | |||
560 | mf1 = mfmatches(node1) |
|
|||
561 |
|
||||
562 | for fn in mf2: |
|
553 | for fn in mf2: | |
563 | if mf1.has_key(fn): |
|
554 | if mf1.has_key(fn): | |
564 | if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)): |
|
555 | if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)): | |
@@ -818,7 +809,7 b' class localrepository(object):' | |||||
818 | base[h] = 1 |
|
809 | base[h] = 1 | |
819 |
|
810 | |||
820 | if not unknown: |
|
811 | if not unknown: | |
821 |
return |
|
812 | return [] | |
822 |
|
813 | |||
823 | rep = {} |
|
814 | rep = {} | |
824 | reqcnt = 0 |
|
815 | reqcnt = 0 | |
@@ -1645,10 +1636,12 b' class localrepository(object):' | |||||
1645 | # merge the tricky bits |
|
1636 | # merge the tricky bits | |
1646 | files = merge.keys() |
|
1637 | files = merge.keys() | |
1647 | files.sort() |
|
1638 | files.sort() | |
|
1639 | xp1 = hex(p1) | |||
|
1640 | xp2 = hex(p2) | |||
1648 | for f in files: |
|
1641 | for f in files: | |
1649 | self.ui.status(_("merging %s\n") % f) |
|
1642 | self.ui.status(_("merging %s\n") % f) | |
1650 | my, other, flag = merge[f] |
|
1643 | my, other, flag = merge[f] | |
1651 | ret = self.merge3(f, my, other) |
|
1644 | ret = self.merge3(f, my, other, xp1, xp2) | |
1652 | if ret: |
|
1645 | if ret: | |
1653 | err = True |
|
1646 | err = True | |
1654 | util.set_exec(self.wjoin(f), flag) |
|
1647 | util.set_exec(self.wjoin(f), flag) | |
@@ -1669,6 +1662,7 b' class localrepository(object):' | |||||
1669 | remove.sort() |
|
1662 | remove.sort() | |
1670 | for f in remove: |
|
1663 | for f in remove: | |
1671 | self.ui.note(_("removing %s\n") % f) |
|
1664 | self.ui.note(_("removing %s\n") % f) | |
|
1665 | util.audit_path(f) | |||
1672 | try: |
|
1666 | try: | |
1673 | util.unlink(self.wjoin(f)) |
|
1667 | util.unlink(self.wjoin(f)) | |
1674 | except OSError, inst: |
|
1668 | except OSError, inst: | |
@@ -1685,7 +1679,7 b' class localrepository(object):' | |||||
1685 | self.dirstate.setparents(p1, p2) |
|
1679 | self.dirstate.setparents(p1, p2) | |
1686 | return err |
|
1680 | return err | |
1687 |
|
1681 | |||
1688 | def merge3(self, fn, my, other): |
|
1682 | def merge3(self, fn, my, other, p1, p2): | |
1689 | """perform a 3-way merge in the working directory""" |
|
1683 | """perform a 3-way merge in the working directory""" | |
1690 |
|
1684 | |||
1691 | def temp(prefix, node): |
|
1685 | def temp(prefix, node): | |
@@ -1708,7 +1702,13 b' class localrepository(object):' | |||||
1708 |
|
1702 | |||
1709 | cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge") |
|
1703 | cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge") | |
1710 | or "hgmerge") |
|
1704 | or "hgmerge") | |
1711 |
r = |
|
1705 | r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root, | |
|
1706 | environ={'HG_FILE': fn, | |||
|
1707 | 'HG_MY_NODE': p1, | |||
|
1708 | 'HG_OTHER_NODE': p2, | |||
|
1709 | 'HG_FILE_MY_NODE': hex(my), | |||
|
1710 | 'HG_FILE_OTHER_NODE': hex(other), | |||
|
1711 | 'HG_FILE_BASE_NODE': hex(base)}) | |||
1712 | if r: |
|
1712 | if r: | |
1713 | self.ui.warn(_("merging %s failed!\n") % fn) |
|
1713 | self.ui.warn(_("merging %s failed!\n") % fn) | |
1714 |
|
1714 | |||
@@ -1759,6 +1759,7 b' class localrepository(object):' | |||||
1759 | raise |
|
1759 | raise | |
1760 | except Exception, inst: |
|
1760 | except Exception, inst: | |
1761 | err(_("unpacking changeset %s: %s") % (short(n), inst)) |
|
1761 | err(_("unpacking changeset %s: %s") % (short(n), inst)) | |
|
1762 | continue | |||
1762 |
|
1763 | |||
1763 | neededmanifests[changes[0]] = n |
|
1764 | neededmanifests[changes[0]] = n | |
1764 |
|
1765 | |||
@@ -1796,10 +1797,14 b' class localrepository(object):' | |||||
1796 | raise |
|
1797 | raise | |
1797 | except Exception, inst: |
|
1798 | except Exception, inst: | |
1798 | err(_("unpacking manifest %s: %s") % (short(n), inst)) |
|
1799 | err(_("unpacking manifest %s: %s") % (short(n), inst)) | |
|
1800 | continue | |||
1799 |
|
1801 | |||
|
1802 | try: | |||
1800 | ff = [ l.split('\0') for l in delta.splitlines() ] |
|
1803 | ff = [ l.split('\0') for l in delta.splitlines() ] | |
1801 | for f, fn in ff: |
|
1804 | for f, fn in ff: | |
1802 | filenodes.setdefault(f, {})[bin(fn[:40])] = 1 |
|
1805 | filenodes.setdefault(f, {})[bin(fn[:40])] = 1 | |
|
1806 | except (ValueError, TypeError), inst: | |||
|
1807 | err(_("broken delta in manifest %s: %s") % (short(n), inst)) | |||
1803 |
|
1808 | |||
1804 | self.ui.status(_("crosschecking files in changesets and manifests\n")) |
|
1809 | self.ui.status(_("crosschecking files in changesets and manifests\n")) | |
1805 |
|
1810 | |||
@@ -1823,6 +1828,9 b' class localrepository(object):' | |||||
1823 | if f == "/dev/null": |
|
1828 | if f == "/dev/null": | |
1824 | continue |
|
1829 | continue | |
1825 | files += 1 |
|
1830 | files += 1 | |
|
1831 | if not f: | |||
|
1832 | err(_("file without name in manifest %s") % short(n)) | |||
|
1833 | continue | |||
1826 | fl = self.file(f) |
|
1834 | fl = self.file(f) | |
1827 | checksize(fl, f) |
|
1835 | checksize(fl, f) | |
1828 |
|
1836 | |||
@@ -1840,7 +1848,7 b' class localrepository(object):' | |||||
1840 | del filenodes[f][n] |
|
1848 | del filenodes[f][n] | |
1841 |
|
1849 | |||
1842 | flr = fl.linkrev(n) |
|
1850 | flr = fl.linkrev(n) | |
1843 |
if flr not in filelinkrevs[ |
|
1851 | if flr not in filelinkrevs.get(f, []): | |
1844 | err(_("%s:%s points to unexpected changeset %d") |
|
1852 | err(_("%s:%s points to unexpected changeset %d") | |
1845 | % (f, short(n), flr)) |
|
1853 | % (f, short(n), flr)) | |
1846 | else: |
|
1854 | else: | |
@@ -1875,3 +1883,13 b' class localrepository(object):' | |||||
1875 | if errors[0]: |
|
1883 | if errors[0]: | |
1876 | self.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) |
|
1884 | self.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) | |
1877 | return 1 |
|
1885 | return 1 | |
|
1886 | ||||
|
1887 | # used to avoid circular references so destructors work | |||
|
1888 | def aftertrans(base): | |||
|
1889 | p = base | |||
|
1890 | def a(): | |||
|
1891 | util.rename(os.path.join(p, "journal"), os.path.join(p, "undo")) | |||
|
1892 | util.rename(os.path.join(p, "journal.dirstate"), | |||
|
1893 | os.path.join(p, "undo.dirstate")) | |||
|
1894 | return a | |||
|
1895 |
@@ -5,8 +5,8 b'' | |||||
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 errno, os, time |
|
8 | from demandload import * | |
9 | import util |
|
9 | demandload(globals(), 'errno os socket time util') | |
10 |
|
10 | |||
11 | class LockException(Exception): |
|
11 | class LockException(Exception): | |
12 | pass |
|
12 | pass | |
@@ -16,11 +16,22 b' class LockUnavailable(LockException):' | |||||
16 | pass |
|
16 | pass | |
17 |
|
17 | |||
18 | class lock(object): |
|
18 | class lock(object): | |
|
19 | # lock is symlink on platforms that support it, file on others. | |||
|
20 | ||||
|
21 | # symlink is used because create of directory entry and contents | |||
|
22 | # are atomic even over nfs. | |||
|
23 | ||||
|
24 | # old-style lock: symlink to pid | |||
|
25 | # new-style lock: symlink to hostname:pid | |||
|
26 | ||||
19 | def __init__(self, file, timeout=-1, releasefn=None): |
|
27 | def __init__(self, file, timeout=-1, releasefn=None): | |
20 | self.f = file |
|
28 | self.f = file | |
21 | self.held = 0 |
|
29 | self.held = 0 | |
22 | self.timeout = timeout |
|
30 | self.timeout = timeout | |
23 | self.releasefn = releasefn |
|
31 | self.releasefn = releasefn | |
|
32 | self.id = None | |||
|
33 | self.host = None | |||
|
34 | self.pid = None | |||
24 | self.lock() |
|
35 | self.lock() | |
25 |
|
36 | |||
26 | def __del__(self): |
|
37 | def __del__(self): | |
@@ -41,16 +52,51 b' class lock(object):' | |||||
41 | raise inst |
|
52 | raise inst | |
42 |
|
53 | |||
43 | def trylock(self): |
|
54 | def trylock(self): | |
44 | pid = os.getpid() |
|
55 | if self.id is None: | |
|
56 | self.host = socket.gethostname() | |||
|
57 | self.pid = os.getpid() | |||
|
58 | self.id = '%s:%s' % (self.host, self.pid) | |||
|
59 | while not self.held: | |||
45 | try: |
|
60 | try: | |
46 |
util.makelock(s |
|
61 | util.makelock(self.id, self.f) | |
47 | self.held = 1 |
|
62 | self.held = 1 | |
48 | except (OSError, IOError), why: |
|
63 | except (OSError, IOError), why: | |
49 | if why.errno == errno.EEXIST: |
|
64 | if why.errno == errno.EEXIST: | |
50 |
|
|
65 | locker = self.testlock() | |
|
66 | if locker: | |||
|
67 | raise LockHeld(locker) | |||
51 | else: |
|
68 | else: | |
52 | raise LockUnavailable(why) |
|
69 | raise LockUnavailable(why) | |
53 |
|
70 | |||
|
71 | def testlock(self): | |||
|
72 | '''return id of locker if lock is valid, else None.''' | |||
|
73 | # if old-style lock, we cannot tell what machine locker is on. | |||
|
74 | # with new-style lock, if locker is on this machine, we can | |||
|
75 | # see if locker is alive. if locker is on this machine but | |||
|
76 | # not alive, we can safely break lock. | |||
|
77 | locker = util.readlock(self.f) | |||
|
78 | c = locker.find(':') | |||
|
79 | if c == -1: | |||
|
80 | return locker | |||
|
81 | host = locker[:c] | |||
|
82 | if host != self.host: | |||
|
83 | return locker | |||
|
84 | try: | |||
|
85 | pid = int(locker[c+1:]) | |||
|
86 | except: | |||
|
87 | return locker | |||
|
88 | if util.testpid(pid): | |||
|
89 | return locker | |||
|
90 | # if locker dead, break lock. must do this with another lock | |||
|
91 | # held, or can race and break valid lock. | |||
|
92 | try: | |||
|
93 | l = lock(self.f + '.break') | |||
|
94 | l.trylock() | |||
|
95 | os.unlink(self.f) | |||
|
96 | l.release() | |||
|
97 | except (LockHeld, LockUnavailable): | |||
|
98 | return locker | |||
|
99 | ||||
54 | def release(self): |
|
100 | def release(self): | |
55 | if self.held: |
|
101 | if self.held: | |
56 | self.held = 0 |
|
102 | self.held = 0 |
@@ -16,8 +16,14 b' def demandload(scope, modules):' | |||||
16 | """ fake demandload function that collects the required modules """ |
|
16 | """ fake demandload function that collects the required modules """ | |
17 | for m in modules.split(): |
|
17 | for m in modules.split(): | |
18 | mod = None |
|
18 | mod = None | |
19 | mod = __import__(m,scope,scope) |
|
19 | try: | |
20 | scope[m] = mod |
|
20 | module, submodules = m.split(':') | |
|
21 | submodules = submodules.split(',') | |||
|
22 | except: | |||
|
23 | module = m | |||
|
24 | submodules = [] | |||
|
25 | mod = __import__(module, scope, scope, submodules) | |||
|
26 | scope[module] = mod | |||
21 | requiredmodules[mod.__name__] = 1 |
|
27 | requiredmodules[mod.__name__] = 1 | |
22 |
|
28 | |||
23 | def getmodules(libpath,packagename): |
|
29 | def getmodules(libpath,packagename): |
@@ -48,7 +48,7 b' def decompress(bin):' | |||||
48 | if t == '\0': return bin |
|
48 | if t == '\0': return bin | |
49 | if t == 'x': return zlib.decompress(bin) |
|
49 | if t == 'x': return zlib.decompress(bin) | |
50 | if t == 'u': return bin[1:] |
|
50 | if t == 'u': return bin[1:] | |
51 |
raise RevlogError(_("unknown compression type % |
|
51 | raise RevlogError(_("unknown compression type %r") % t) | |
52 |
|
52 | |||
53 | indexformat = ">4l20s20s20s" |
|
53 | indexformat = ">4l20s20s20s" | |
54 |
|
54 |
@@ -15,8 +15,10 b' class rangereader(httprangereader.httpra' | |||||
15 | def read(self, size=None): |
|
15 | def read(self, size=None): | |
16 | try: |
|
16 | try: | |
17 | return httprangereader.httprangereader.read(self, size) |
|
17 | return httprangereader.httprangereader.read(self, size) | |
|
18 | except urllib2.HTTPError, inst: | |||
|
19 | raise IOError(None, inst) | |||
18 | except urllib2.URLError, inst: |
|
20 | except urllib2.URLError, inst: | |
19 |
raise IOError(None, |
|
21 | raise IOError(None, inst.reason[1]) | |
20 |
|
22 | |||
21 | def opener(base): |
|
23 | def opener(base): | |
22 | """return a function that opens files over http""" |
|
24 | """return a function that opens files over http""" |
@@ -22,6 +22,7 b' class transaction(object):' | |||||
22 | if os.path.exists(journal): |
|
22 | if os.path.exists(journal): | |
23 | raise AssertionError(_("journal already exists - run hg recover")) |
|
23 | raise AssertionError(_("journal already exists - run hg recover")) | |
24 |
|
24 | |||
|
25 | self.count = 1 | |||
25 | self.report = report |
|
26 | self.report = report | |
26 | self.opener = opener |
|
27 | self.opener = opener | |
27 | self.after = after |
|
28 | self.after = after | |
@@ -46,7 +47,17 b' class transaction(object):' | |||||
46 | self.file.write("%s\0%d\n" % (file, offset)) |
|
47 | self.file.write("%s\0%d\n" % (file, offset)) | |
47 | self.file.flush() |
|
48 | self.file.flush() | |
48 |
|
49 | |||
|
50 | def nest(self): | |||
|
51 | self.count += 1 | |||
|
52 | return self | |||
|
53 | ||||
|
54 | def running(self): | |||
|
55 | return self.count > 0 | |||
|
56 | ||||
49 | def close(self): |
|
57 | def close(self): | |
|
58 | self.count -= 1 | |||
|
59 | if self.count != 0: | |||
|
60 | return | |||
50 | self.file.close() |
|
61 | self.file.close() | |
51 | self.entries = [] |
|
62 | self.entries = [] | |
52 | if self.after: |
|
63 | if self.after: |
@@ -5,15 +5,18 b'' | |||||
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 |
|
8 | import ConfigParser | |
9 | from i18n import gettext as _ |
|
9 | from i18n import gettext as _ | |
10 | from demandload import * |
|
10 | from demandload import * | |
11 | demandload(globals(), "re socket sys util") |
|
11 | demandload(globals(), "os re socket sys util") | |
12 |
|
12 | |||
13 | class ui(object): |
|
13 | class ui(object): | |
14 | def __init__(self, verbose=False, debug=False, quiet=False, |
|
14 | def __init__(self, verbose=False, debug=False, quiet=False, | |
15 | interactive=True): |
|
15 | interactive=True, parentui=None): | |
16 | self.overlay = {} |
|
16 | self.overlay = {} | |
|
17 | if parentui is None: | |||
|
18 | # this is the parent of all ui children | |||
|
19 | self.parentui = None | |||
17 | self.cdata = ConfigParser.SafeConfigParser() |
|
20 | self.cdata = ConfigParser.SafeConfigParser() | |
18 | self.readconfig(util.rcpath) |
|
21 | self.readconfig(util.rcpath) | |
19 |
|
22 | |||
@@ -24,6 +27,19 b' class ui(object):' | |||||
24 |
|
27 | |||
25 | self.updateopts(verbose, debug, quiet, interactive) |
|
28 | self.updateopts(verbose, debug, quiet, interactive) | |
26 | self.diffcache = None |
|
29 | self.diffcache = None | |
|
30 | else: | |||
|
31 | # parentui may point to an ui object which is already a child | |||
|
32 | self.parentui = parentui.parentui or parentui | |||
|
33 | parent_cdata = self.parentui.cdata | |||
|
34 | self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults()) | |||
|
35 | # make interpolation work | |||
|
36 | for section in parent_cdata.sections(): | |||
|
37 | self.cdata.add_section(section) | |||
|
38 | for name, value in parent_cdata.items(section, raw=True): | |||
|
39 | self.cdata.set(section, name, value) | |||
|
40 | ||||
|
41 | def __getattr__(self, key): | |||
|
42 | return getattr(self.parentui, key) | |||
27 |
|
43 | |||
28 | def updateopts(self, verbose=False, debug=False, quiet=False, |
|
44 | def updateopts(self, verbose=False, debug=False, quiet=False, | |
29 | interactive=True): |
|
45 | interactive=True): | |
@@ -32,7 +48,7 b' class ui(object):' | |||||
32 | self.debugflag = (self.debugflag or debug) |
|
48 | self.debugflag = (self.debugflag or debug) | |
33 | self.interactive = (self.interactive and interactive) |
|
49 | self.interactive = (self.interactive and interactive) | |
34 |
|
50 | |||
35 | def readconfig(self, fn): |
|
51 | def readconfig(self, fn, root=None): | |
36 | if isinstance(fn, basestring): |
|
52 | if isinstance(fn, basestring): | |
37 | fn = [fn] |
|
53 | fn = [fn] | |
38 | for f in fn: |
|
54 | for f in fn: | |
@@ -40,6 +56,12 b' class ui(object):' | |||||
40 | self.cdata.read(f) |
|
56 | self.cdata.read(f) | |
41 | except ConfigParser.ParsingError, inst: |
|
57 | except ConfigParser.ParsingError, inst: | |
42 | raise util.Abort(_("Failed to parse %s\n%s") % (f, inst)) |
|
58 | raise util.Abort(_("Failed to parse %s\n%s") % (f, inst)) | |
|
59 | # translate paths relative to root (or home) into absolute paths | |||
|
60 | if root is None: | |||
|
61 | root = os.path.expanduser('~') | |||
|
62 | for name, path in self.configitems("paths"): | |||
|
63 | if path.find("://") == -1 and not os.path.isabs(path): | |||
|
64 | self.cdata.set("paths", name, os.path.join(root, path)) | |||
43 |
|
65 | |||
44 | def setconfig(self, section, name, val): |
|
66 | def setconfig(self, section, name, val): | |
45 | self.overlay[(section, name)] = val |
|
67 | self.overlay[(section, name)] = val | |
@@ -48,22 +70,43 b' class ui(object):' | |||||
48 | if self.overlay.has_key((section, name)): |
|
70 | if self.overlay.has_key((section, name)): | |
49 | return self.overlay[(section, name)] |
|
71 | return self.overlay[(section, name)] | |
50 | if self.cdata.has_option(section, name): |
|
72 | if self.cdata.has_option(section, name): | |
|
73 | try: | |||
51 | return self.cdata.get(section, name) |
|
74 | return self.cdata.get(section, name) | |
|
75 | except ConfigParser.InterpolationError, inst: | |||
|
76 | raise util.Abort(_("Error in configuration:\n%s") % inst) | |||
|
77 | if self.parentui is None: | |||
52 | return default |
|
78 | return default | |
|
79 | else: | |||
|
80 | return self.parentui.config(section, name, default) | |||
53 |
|
81 | |||
54 | def configbool(self, section, name, default=False): |
|
82 | def configbool(self, section, name, default=False): | |
55 | if self.overlay.has_key((section, name)): |
|
83 | if self.overlay.has_key((section, name)): | |
56 | return self.overlay[(section, name)] |
|
84 | return self.overlay[(section, name)] | |
57 | if self.cdata.has_option(section, name): |
|
85 | if self.cdata.has_option(section, name): | |
|
86 | try: | |||
58 | return self.cdata.getboolean(section, name) |
|
87 | return self.cdata.getboolean(section, name) | |
|
88 | except ConfigParser.InterpolationError, inst: | |||
|
89 | raise util.Abort(_("Error in configuration:\n%s") % inst) | |||
|
90 | if self.parentui is None: | |||
59 | return default |
|
91 | return default | |
|
92 | else: | |||
|
93 | return self.parentui.configbool(section, name, default) | |||
60 |
|
94 | |||
61 | def configitems(self, section): |
|
95 | def configitems(self, section): | |
|
96 | items = {} | |||
|
97 | if self.parentui is not None: | |||
|
98 | items = dict(self.parentui.configitems(section)) | |||
62 | if self.cdata.has_section(section): |
|
99 | if self.cdata.has_section(section): | |
63 | return self.cdata.items(section) |
|
100 | try: | |
64 | return [] |
|
101 | items.update(dict(self.cdata.items(section))) | |
|
102 | except ConfigParser.InterpolationError, inst: | |||
|
103 | raise util.Abort(_("Error in configuration:\n%s") % inst) | |||
|
104 | x = items.items() | |||
|
105 | x.sort() | |||
|
106 | return x | |||
65 |
|
107 | |||
66 | def walkconfig(self): |
|
108 | def walkconfig(self, seen=None): | |
|
109 | if seen is None: | |||
67 | seen = {} |
|
110 | seen = {} | |
68 | for (section, name), value in self.overlay.iteritems(): |
|
111 | for (section, name), value in self.overlay.iteritems(): | |
69 | yield section, name, value |
|
112 | yield section, name, value | |
@@ -73,6 +116,9 b' class ui(object):' | |||||
73 | if (section, name) in seen: continue |
|
116 | if (section, name) in seen: continue | |
74 | yield section, name, value.replace('\n', '\\n') |
|
117 | yield section, name, value.replace('\n', '\\n') | |
75 | seen[section, name] = 1 |
|
118 | seen[section, name] = 1 | |
|
119 | if self.parentui is not None: | |||
|
120 | for parent in self.parentui.walkconfig(seen): | |||
|
121 | yield parent | |||
76 |
|
122 | |||
77 | def extensions(self): |
|
123 | def extensions(self): | |
78 | return self.configitems("extensions") |
|
124 | return self.configitems("extensions") | |
@@ -107,15 +153,12 b' class ui(object):' | |||||
107 | if not self.verbose: user = util.shortuser(user) |
|
153 | if not self.verbose: user = util.shortuser(user) | |
108 | return user |
|
154 | return user | |
109 |
|
155 | |||
110 |
def expandpath(self, loc |
|
156 | def expandpath(self, loc): | |
111 | paths = {} |
|
157 | """Return repository location relative to cwd or from [paths]""" | |
112 | for name, path in self.configitems("paths"): |
|
158 | if loc.find("://") != -1 or os.path.exists(loc): | |
113 | m = path.find("://") |
|
159 | return loc | |
114 | if m == -1: |
|
|||
115 | path = os.path.join(root, path) |
|
|||
116 | paths[name] = path |
|
|||
117 |
|
160 | |||
118 |
return paths |
|
161 | return self.config("paths", loc, loc) | |
119 |
|
162 | |||
120 | def write(self, *args): |
|
163 | def write(self, *args): | |
121 | for a in args: |
|
164 | for a in args: | |
@@ -126,6 +169,12 b' class ui(object):' | |||||
126 | for a in args: |
|
169 | for a in args: | |
127 | sys.stderr.write(str(a)) |
|
170 | sys.stderr.write(str(a)) | |
128 |
|
171 | |||
|
172 | def flush(self): | |||
|
173 | try: | |||
|
174 | sys.stdout.flush() | |||
|
175 | finally: | |||
|
176 | sys.stderr.flush() | |||
|
177 | ||||
129 | def readline(self): |
|
178 | def readline(self): | |
130 | return sys.stdin.readline()[:-1] |
|
179 | return sys.stdin.readline()[:-1] | |
131 | def prompt(self, msg, pat, default="y"): |
|
180 | def prompt(self, msg, pat, default="y"): | |
@@ -157,7 +206,9 b' class ui(object):' | |||||
157 | os.environ.get("EDITOR", "vi")) |
|
206 | os.environ.get("EDITOR", "vi")) | |
158 |
|
207 | |||
159 | os.environ["HGUSER"] = self.username() |
|
208 | os.environ["HGUSER"] = self.username() | |
160 |
util.system("%s \"%s\"" % (editor, name), |
|
209 | util.system("%s \"%s\"" % (editor, name), | |
|
210 | environ={'HGUSER': self.username()}, | |||
|
211 | onerr=util.Abort, errprefix=_("edit failed")) | |||
161 |
|
212 | |||
162 | t = open(name).read() |
|
213 | t = open(name).read() | |
163 | t = re.sub("(?m)^HG:.*\n", "", t) |
|
214 | t = re.sub("(?m)^HG:.*\n", "", t) |
@@ -315,15 +315,42 b' def _matcher(canonroot, cwd, names, inc,' | |||||
315 | (files and filematch(fn)))), |
|
315 | (files and filematch(fn)))), | |
316 | (inc or exc or (pats and pats != [('glob', '**')])) and True) |
|
316 | (inc or exc or (pats and pats != [('glob', '**')])) and True) | |
317 |
|
317 | |||
318 | def system(cmd, errprefix=None): |
|
318 | def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None): | |
319 | """execute a shell command that must succeed""" |
|
319 | '''enhanced shell command execution. | |
|
320 | run with environment maybe modified, maybe in different dir. | |||
|
321 | ||||
|
322 | if command fails and onerr is None, return status. if ui object, | |||
|
323 | print error message and return status, else raise onerr object as | |||
|
324 | exception.''' | |||
|
325 | oldenv = {} | |||
|
326 | for k in environ: | |||
|
327 | oldenv[k] = os.environ.get(k) | |||
|
328 | if cwd is not None: | |||
|
329 | oldcwd = os.getcwd() | |||
|
330 | try: | |||
|
331 | for k, v in environ.iteritems(): | |||
|
332 | os.environ[k] = str(v) | |||
|
333 | if cwd is not None and oldcwd != cwd: | |||
|
334 | os.chdir(cwd) | |||
320 | rc = os.system(cmd) |
|
335 | rc = os.system(cmd) | |
321 | if rc: |
|
336 | if rc and onerr: | |
322 |
errmsg = |
|
337 | errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]), | |
323 | explain_exit(rc)[0]) |
|
338 | explain_exit(rc)[0]) | |
324 | if errprefix: |
|
339 | if errprefix: | |
325 |
errmsg = |
|
340 | errmsg = '%s: %s' % (errprefix, errmsg) | |
326 | raise Abort(errmsg) |
|
341 | try: | |
|
342 | onerr.warn(errmsg + '\n') | |||
|
343 | except AttributeError: | |||
|
344 | raise onerr(errmsg) | |||
|
345 | return rc | |||
|
346 | finally: | |||
|
347 | for k, v in oldenv.iteritems(): | |||
|
348 | if v is None: | |||
|
349 | del os.environ[k] | |||
|
350 | else: | |||
|
351 | os.environ[k] = v | |||
|
352 | if cwd is not None and oldcwd != cwd: | |||
|
353 | os.chdir(oldcwd) | |||
327 |
|
354 | |||
328 | def rename(src, dst): |
|
355 | def rename(src, dst): | |
329 | """forcibly rename a file""" |
|
356 | """forcibly rename a file""" | |
@@ -363,7 +390,14 b' def copyfiles(src, dst, hardlink=None):' | |||||
363 | else: |
|
390 | else: | |
364 | shutil.copy(src, dst) |
|
391 | shutil.copy(src, dst) | |
365 |
|
392 | |||
366 | def opener(base): |
|
393 | def audit_path(path): | |
|
394 | """Abort if path contains dangerous components""" | |||
|
395 | parts = os.path.normcase(path).split(os.sep) | |||
|
396 | if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '') | |||
|
397 | or os.pardir in parts): | |||
|
398 | raise Abort(_("path contains illegal component: %s\n") % path) | |||
|
399 | ||||
|
400 | def opener(base, audit=True): | |||
367 | """ |
|
401 | """ | |
368 | return a function that opens files relative to base |
|
402 | return a function that opens files relative to base | |
369 |
|
403 | |||
@@ -371,6 +405,7 b' def opener(base):' | |||||
371 | remote file access from higher level code. |
|
405 | remote file access from higher level code. | |
372 | """ |
|
406 | """ | |
373 | p = base |
|
407 | p = base | |
|
408 | audit_p = audit | |||
374 |
|
409 | |||
375 | def mktempcopy(name): |
|
410 | def mktempcopy(name): | |
376 | d, fn = os.path.split(name) |
|
411 | d, fn = os.path.split(name) | |
@@ -401,6 +436,8 b' def opener(base):' | |||||
401 | self.close() |
|
436 | self.close() | |
402 |
|
437 | |||
403 | def o(path, mode="r", text=False, atomic=False): |
|
438 | def o(path, mode="r", text=False, atomic=False): | |
|
439 | if audit_p: | |||
|
440 | audit_path(path) | |||
404 | f = os.path.join(p, path) |
|
441 | f = os.path.join(p, path) | |
405 |
|
442 | |||
406 | if not text: |
|
443 | if not text: | |
@@ -489,7 +526,7 b" if os.name == 'nt':" | |||||
489 | return pf |
|
526 | return pf | |
490 |
|
527 | |||
491 | try: # ActivePython can create hard links using win32file module |
|
528 | try: # ActivePython can create hard links using win32file module | |
492 | import win32file |
|
529 | import win32api, win32con, win32file | |
493 |
|
530 | |||
494 | def os_link(src, dst): # NB will only succeed on NTFS |
|
531 | def os_link(src, dst): # NB will only succeed on NTFS | |
495 | win32file.CreateHardLink(dst, src) |
|
532 | win32file.CreateHardLink(dst, src) | |
@@ -506,8 +543,18 b" if os.name == 'nt':" | |||||
506 | except: |
|
543 | except: | |
507 | return os.stat(pathname).st_nlink |
|
544 | return os.stat(pathname).st_nlink | |
508 |
|
545 | |||
|
546 | def testpid(pid): | |||
|
547 | '''return False if pid is dead, True if running or not known''' | |||
|
548 | try: | |||
|
549 | win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, | |||
|
550 | False, pid) | |||
|
551 | except: | |||
|
552 | return True | |||
|
553 | ||||
509 | except ImportError: |
|
554 | except ImportError: | |
510 | pass |
|
555 | def testpid(pid): | |
|
556 | '''return False if pid dead, True if running or not known''' | |||
|
557 | return True | |||
511 |
|
558 | |||
512 | def is_exec(f, last): |
|
559 | def is_exec(f, last): | |
513 | return last |
|
560 | return last | |
@@ -604,6 +651,14 b' else:' | |||||
604 | else: |
|
651 | else: | |
605 | raise |
|
652 | raise | |
606 |
|
653 | |||
|
654 | def testpid(pid): | |||
|
655 | '''return False if pid dead, True if running or not sure''' | |||
|
656 | try: | |||
|
657 | os.kill(pid, 0) | |||
|
658 | return True | |||
|
659 | except OSError, inst: | |||
|
660 | return inst.errno != errno.ESRCH | |||
|
661 | ||||
607 | def explain_exit(code): |
|
662 | def explain_exit(code): | |
608 | """return a 2-tuple (desc, code) describing a process's status""" |
|
663 | """return a 2-tuple (desc, code) describing a process's status""" | |
609 | if os.WIFEXITED(code): |
|
664 | if os.WIFEXITED(code): | |
@@ -700,3 +755,16 b' def shortuser(user):' | |||||
700 | if f >= 0: |
|
755 | if f >= 0: | |
701 | user = user[f+1:] |
|
756 | user = user[f+1:] | |
702 | return user |
|
757 | return user | |
|
758 | ||||
|
759 | def walkrepos(path): | |||
|
760 | '''yield every hg repository under path, recursively.''' | |||
|
761 | def errhandler(err): | |||
|
762 | if err.filename == path: | |||
|
763 | raise err | |||
|
764 | ||||
|
765 | for root, dirs, files in os.walk(path, onerror=errhandler): | |||
|
766 | for d in dirs: | |||
|
767 | if d == '.hg': | |||
|
768 | yield root | |||
|
769 | dirs[:] = [] | |||
|
770 | break |
@@ -5,8 +5,11 b'' | |||||
5 | # './setup.py install', or |
|
5 | # './setup.py install', or | |
6 | # './setup.py --help' for more options |
|
6 | # './setup.py --help' for more options | |
7 |
|
7 | |||
|
8 | import sys | |||
|
9 | if not hasattr(sys, 'version_info') or sys.version_info < (2, 3): | |||
|
10 | raise SystemExit, "Mercurial requires python 2.3 or later." | |||
|
11 | ||||
8 | import glob |
|
12 | import glob | |
9 | import sys |
|
|||
10 | from distutils.core import setup, Extension |
|
13 | from distutils.core import setup, Extension | |
11 | from distutils.command.install_data import install_data |
|
14 | from distutils.command.install_data import install_data | |
12 |
|
15 |
@@ -53,7 +53,16 b' else' | |||||
53 | fi |
|
53 | fi | |
54 | cd "$TESTDIR" |
|
54 | cd "$TESTDIR" | |
55 |
|
55 | |||
56 | PATH="$INST/bin:$PATH"; export PATH |
|
56 | BINDIR="$INST/bin" | |
|
57 | PATH="$BINDIR:$PATH"; export PATH | |||
|
58 | if [ -n "$PYTHON" ]; then | |||
|
59 | { | |||
|
60 | echo "#!/bin/sh" | |||
|
61 | echo "exec \"$PYTHON"'" "$@"' | |||
|
62 | } > "$BINDIR/python" | |||
|
63 | chmod 755 "$BINDIR/python" | |||
|
64 | fi | |||
|
65 | ||||
57 | PYTHONPATH="$PYTHONDIR"; export PYTHONPATH |
|
66 | PYTHONPATH="$PYTHONDIR"; export PYTHONPATH | |
58 |
|
67 | |||
59 | run_one() { |
|
68 | run_one() { |
@@ -1,5 +1,3 b'' | |||||
1 | transaction abort! |
|
|||
2 | rollback completed |
|
|||
3 | abort: impossible time zone offset: 4444444 |
|
1 | abort: impossible time zone offset: 4444444 | |
4 | transaction abort! |
|
2 | transaction abort! | |
5 | rollback completed |
|
3 | rollback completed | |
@@ -13,4 +11,6 b" abort: invalid date: ' 1 4444'" | |||||
13 | transaction abort! |
|
11 | transaction abort! | |
14 | rollback completed |
|
12 | rollback completed | |
15 | abort: date exceeds 32 bits: 111111111111 |
|
13 | abort: date exceeds 32 bits: 111111111111 | |
|
14 | transaction abort! | |||
|
15 | rollback completed | |||
16 | abort: No such file or directory: .../test/bar |
|
16 | abort: No such file or directory: .../test/bar |
@@ -64,7 +64,6 b' list of commands (use "hg help -v" to sh' | |||||
64 | paths show definition of symbolic path names |
|
64 | paths show definition of symbolic path names | |
65 | pull pull changes from the specified source |
|
65 | pull pull changes from the specified source | |
66 | push push changes to the specified destination |
|
66 | push push changes to the specified destination | |
67 | rawcommit raw commit interface (DEPRECATED) |
|
|||
68 | recover roll back an interrupted transaction |
|
67 | recover roll back an interrupted transaction | |
69 | remove remove the specified files on the next commit |
|
68 | remove remove the specified files on the next commit | |
70 | rename rename files; equivalent of copy + remove |
|
69 | rename rename files; equivalent of copy + remove | |
@@ -106,7 +105,6 b' list of commands (use "hg help -v" to sh' | |||||
106 | paths show definition of symbolic path names |
|
105 | paths show definition of symbolic path names | |
107 | pull pull changes from the specified source |
|
106 | pull pull changes from the specified source | |
108 | push push changes to the specified destination |
|
107 | push push changes to the specified destination | |
109 | rawcommit raw commit interface (DEPRECATED) |
|
|||
110 | recover roll back an interrupted transaction |
|
108 | recover roll back an interrupted transaction | |
111 | remove remove the specified files on the next commit |
|
109 | remove remove the specified files on the next commit | |
112 | rename rename files; equivalent of copy + remove |
|
110 | rename rename files; equivalent of copy + remove | |
@@ -173,9 +171,9 b' options:' | |||||
173 |
|
171 | |||
174 | -r --rev revision |
|
172 | -r --rev revision | |
175 | -a --text treat all files as text |
|
173 | -a --text treat all files as text | |
176 | -I --include include names matching the given patterns |
|
|||
177 | -p --show-function show which function each change is in |
|
174 | -p --show-function show which function each change is in | |
178 | -w --ignore-all-space ignore white space when comparing lines |
|
175 | -w --ignore-all-space ignore white space when comparing lines | |
|
176 | -I --include include names matching the given patterns | |||
179 | -X --exclude exclude names matching the given patterns |
|
177 | -X --exclude exclude names matching the given patterns | |
180 | hg status [OPTION]... [FILE]... |
|
178 | hg status [OPTION]... [FILE]... | |
181 |
|
179 |
@@ -1,23 +1,23 b'' | |||||
1 | precommit hook: p1=0000000000000000000000000000000000000000 p2= |
|
1 | precommit hook: p1=0000000000000000000000000000000000000000 p2= | |
2 | pretxncommit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2= |
|
2 | pretxncommit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2= | |
3 | 0:cb9a9f314b8b |
|
3 | 0:cb9a9f314b8b | |
|
4 | commit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2= | |||
4 | commit hook b |
|
5 | commit hook b | |
5 | commit hook: n=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p1=0000000000000000000000000000000000000000 p2= |
|
|||
6 | precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= |
|
6 | precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= | |
7 | pretxncommit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= |
|
7 | pretxncommit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= | |
8 | 1:ab228980c14d |
|
8 | 1:ab228980c14d | |
|
9 | commit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= | |||
9 | commit hook b |
|
10 | commit hook b | |
10 | commit hook: n=ab228980c14deea8b9555d91c9581127383e40fd p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= |
|
|||
11 | precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= |
|
11 | precommit hook: p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= | |
12 | pretxncommit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= |
|
12 | pretxncommit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= | |
13 | 2:ee9deb46ab31 |
|
13 | 2:ee9deb46ab31 | |
|
14 | commit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= | |||
14 | commit hook b |
|
15 | commit hook b | |
15 | commit hook: n=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b p2= |
|
|||
16 | precommit hook: p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd |
|
16 | precommit hook: p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd | |
17 | pretxncommit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd |
|
17 | pretxncommit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd | |
18 | 3:07f3376c1e65 |
|
18 | 3:07f3376c1e65 | |
|
19 | commit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd | |||
19 | commit hook b |
|
20 | commit hook b | |
20 | commit hook: n=07f3376c1e655977439df2a814e3cc14b27abac2 p1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 p2=ab228980c14deea8b9555d91c9581127383e40fd |
|
|||
21 | prechangegroup hook |
|
21 | prechangegroup hook | |
22 | changegroup hook: n=ab228980c14deea8b9555d91c9581127383e40fd |
|
22 | changegroup hook: n=ab228980c14deea8b9555d91c9581127383e40fd | |
23 | incoming hook: n=ab228980c14deea8b9555d91c9581127383e40fd |
|
23 | incoming hook: n=ab228980c14deea8b9555d91c9581127383e40fd | |
@@ -34,8 +34,8 b' pretag hook: t=a n=07f3376c1e655977439df' | |||||
34 | precommit hook: p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2= |
|
34 | precommit hook: p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2= | |
35 | pretxncommit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2= |
|
35 | pretxncommit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2= | |
36 | 4:3cd2c6a5a36c |
|
36 | 4:3cd2c6a5a36c | |
|
37 | commit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2= | |||
37 | commit hook b |
|
38 | commit hook b | |
38 | commit hook: n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p1=07f3376c1e655977439df2a814e3cc14b27abac2 p2= |
|
|||
39 | tag hook: t=a n=07f3376c1e655977439df2a814e3cc14b27abac2 l=0 |
|
39 | tag hook: t=a n=07f3376c1e655977439df2a814e3cc14b27abac2 l=0 | |
40 | pretag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1 |
|
40 | pretag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1 | |
41 | tag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1 |
|
41 | tag hook: t=la n=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 l=1 | |
@@ -47,11 +47,14 b' pretag.forbid hook' | |||||
47 | abort: pretag.forbid hook exited with status 1 |
|
47 | abort: pretag.forbid hook exited with status 1 | |
48 | 4:3cd2c6a5a36c |
|
48 | 4:3cd2c6a5a36c | |
49 | precommit hook: p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2= |
|
49 | precommit hook: p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2= | |
|
50 | pretxncommit hook: n=469a61fe67d64df9a5023e4c2b8a0b85c61e9b69 p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2= | |||
|
51 | 5:469a61fe67d6 | |||
50 | pretxncommit.forbid hook: tip=5:469a61fe67d6 |
|
52 | pretxncommit.forbid hook: tip=5:469a61fe67d6 | |
51 | abort: pretxncommit.forbid hook exited with status 1 |
|
53 | abort: pretxncommit.forbid hook exited with status 1 | |
52 | transaction abort! |
|
54 | transaction abort! | |
53 | rollback completed |
|
55 | rollback completed | |
54 | 4:3cd2c6a5a36c |
|
56 | 4:3cd2c6a5a36c | |
|
57 | precommit hook: p1=3cd2c6a5a36c5908aad3bc0d717c29873a05dfc2 p2= | |||
55 | precommit.forbid hook |
|
58 | precommit.forbid hook | |
56 | abort: precommit.forbid hook exited with status 1 |
|
59 | abort: precommit.forbid hook exited with status 1 | |
57 | 4:3cd2c6a5a36c |
|
60 | 4:3cd2c6a5a36c |
@@ -171,3 +171,11 b' mv d1/bb d1/bc' | |||||
171 | hg rename --after d1/bb d1/bc |
|
171 | hg rename --after d1/bb d1/bc | |
172 | hg status |
|
172 | hg status | |
173 | hg update -C |
|
173 | hg update -C | |
|
174 | ||||
|
175 | echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)" | |||
|
176 | hg rename d1/b d1/bb | |||
|
177 | echo "some stuff added to d1/bb" >> d1/bb | |||
|
178 | hg rename d1/bb d1/b | |||
|
179 | hg status | |||
|
180 | hg debugstate | grep copy | |||
|
181 | hg update -C |
@@ -252,3 +252,5 b' R d1/b' | |||||
252 | # transitive rename --after |
|
252 | # transitive rename --after | |
253 | A d1/bc |
|
253 | A d1/bc | |
254 | R d1/b |
|
254 | R d1/b | |
|
255 | # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b) | |||
|
256 | M d1/b |
@@ -1,4 +1,4 b'' | |||||
1 |
abort: |
|
1 | abort: Connection refused | |
2 | 255 |
|
2 | 255 | |
3 | ls: copy: No such file or directory |
|
3 | ls: copy: No such file or directory | |
4 | changeset: 0:61c9426e69fe |
|
4 | changeset: 0:61c9426e69fe |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now