##// END OF EJS Templates
merge.
Vadim Gelfer -
r2952:6ba3409f merge default
parent child Browse files
Show More
@@ -0,0 +1,45 b''
1 #!/bin/sh
2 # http://www.selenic.com/mercurial/bts/issue322
3
4 echo % file replaced with directory
5
6 hg init a
7 cd a
8 echo a > a
9 hg commit -Ama
10 rm a
11 mkdir a
12 echo a > a/a
13
14 echo % should fail - would corrupt dirstate
15 hg add a/a
16
17 echo % should fail - if add succeeded, would corrupt manifest
18 hg commit -mb
19
20 echo % should fail if commit succeeded - manifest is corrupt
21 hg verify
22
23 cd ..
24 echo % should succeed, but manifest is corrupt
25 hg --debug --traceback clone a b
26
27 echo % directory replaced with file
28
29 hg init c
30 cd c
31 mkdir a
32 echo a > a/a
33 hg commit -Ama
34
35 rm -rf a
36 echo a > a
37
38 echo % should fail - would corrupt dirstate
39 hg add a
40
41 echo % should fail - if add succeeded, would corrupt manifest
42 hg commit -mb a
43
44 echo % should fail if commit succeeded - manifest is corrupt
45 hg verify
@@ -1,39 +1,40 b''
1 ο»ΏAndrea Arcangeli <andrea at suse.de>
1 ο»ΏAndrea Arcangeli <andrea at suse.de>
2 Thomas Arendsen Hein <thomas at intevation.de>
2 Thomas Arendsen Hein <thomas at intevation.de>
3 Goffredo Baroncelli <kreijack at libero.it>
3 Goffredo Baroncelli <kreijack at libero.it>
4 Muli Ben-Yehuda <mulix at mulix.org>
4 Muli Ben-Yehuda <mulix at mulix.org>
5 Mikael Berthe <mikael at lilotux.net>
5 Mikael Berthe <mikael at lilotux.net>
6 Benoit Boissinot <bboissin at gmail.com>
6 Benoit Boissinot <bboissin at gmail.com>
7 Brendan Cully <brendan at kublai.com>
7 Vincent Danjean <vdanjean.ml at free.fr>
8 Vincent Danjean <vdanjean.ml at free.fr>
8 Jake Edge <jake at edge2.net>
9 Jake Edge <jake at edge2.net>
9 Michael Fetterman <michael.fetterman at intel.com>
10 Michael Fetterman <michael.fetterman at intel.com>
10 Edouard Gomez <ed.gomez at free.fr>
11 Edouard Gomez <ed.gomez at free.fr>
11 Eric Hopper <hopper at omnifarious.org>
12 Eric Hopper <hopper at omnifarious.org>
12 Alecs King <alecsk at gmail.com>
13 Alecs King <alecsk at gmail.com>
13 Volker Kleinfeld <Volker.Kleinfeld at gmx.de>
14 Volker Kleinfeld <Volker.Kleinfeld at gmx.de>
14 Vadim Lebedev <vadim at mbdsys.com>
15 Vadim Lebedev <vadim at mbdsys.com>
15 Christopher Li <hg at chrisli.org>
16 Christopher Li <hg at chrisli.org>
16 Chris Mason <mason at suse.com>
17 Chris Mason <mason at suse.com>
17 Colin McMillen <mcmillen at cs.cmu.edu>
18 Colin McMillen <mcmillen at cs.cmu.edu>
18 Wojciech Milkowski <wmilkowski at interia.pl>
19 Wojciech Milkowski <wmilkowski at interia.pl>
19 Chad Netzer <chad.netzer at gmail.com>
20 Chad Netzer <chad.netzer at gmail.com>
20 Bryan O'Sullivan <bos at serpentine.com>
21 Bryan O'Sullivan <bos at serpentine.com>
21 Vicent SeguΓ­ Pascual <vseguip at gmail.com>
22 Vicent SeguΓ­ Pascual <vseguip at gmail.com>
22 Sean Perry <shaleh at speakeasy.net>
23 Sean Perry <shaleh at speakeasy.net>
23 Nguyen Anh Quynh <aquynh at gmail.com>
24 Nguyen Anh Quynh <aquynh at gmail.com>
24 Ollivier Robert <roberto at keltia.freenix.fr>
25 Ollivier Robert <roberto at keltia.freenix.fr>
25 Alexander Schremmer <alex at alexanderweb.de>
26 Alexander Schremmer <alex at alexanderweb.de>
26 Arun Sharma <arun at sharma-home.net>
27 Arun Sharma <arun at sharma-home.net>
27 Josef "Jeff" Sipek <jeffpc at optonline.net>
28 Josef "Jeff" Sipek <jeffpc at optonline.net>
28 Kevin Smith <yarcs at qualitycode.com>
29 Kevin Smith <yarcs at qualitycode.com>
29 TK Soh <teekaysoh at yahoo.com>
30 TK Soh <teekaysoh at yahoo.com>
30 Radoslaw Szkodzinski <astralstorm at gorzow.mm.pl>
31 Radoslaw Szkodzinski <astralstorm at gorzow.mm.pl>
31 Samuel Tardieu <sam at rfc1149.net>
32 Samuel Tardieu <sam at rfc1149.net>
32 K Thananchayan <thananck at yahoo.com>
33 K Thananchayan <thananck at yahoo.com>
33 Andrew Thompson <andrewkt at aktzero.com>
34 Andrew Thompson <andrewkt at aktzero.com>
34 Michael S. Tsirkin <mst at mellanox.co.il>
35 Michael S. Tsirkin <mst at mellanox.co.il>
35 Rafael Villar Burke <pachi at mmn-arquitectos.com>
36 Rafael Villar Burke <pachi at mmn-arquitectos.com>
36 Tristan Wibberley <tristan at wibberley.org>
37 Tristan Wibberley <tristan at wibberley.org>
37 Mark Williamson <mark.williamson at cl.cam.ac.uk>
38 Mark Williamson <mark.williamson at cl.cam.ac.uk>
38
39
39 If you are a contributor and don't see your name here, please let me know.
40 If you are a contributor and don't see your name here, please let me know.
@@ -1,1980 +1,2004 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.demandload import *
32 from mercurial.demandload import *
33 from mercurial.i18n import gettext as _
33 from mercurial.i18n import gettext as _
34 demandload(globals(), "os sys re struct traceback errno bz2")
34 demandload(globals(), "os sys re struct traceback errno bz2")
35 demandload(globals(), "mercurial:cmdutil,commands,hg,patch,revlog,ui,util")
35 demandload(globals(), "mercurial:cmdutil,commands,hg,patch,revlog,ui,util")
36
36
37 commands.norepo += " qclone qversion"
37 commands.norepo += " qclone qversion"
38
38
39 class statusentry:
39 class statusentry:
40 def __init__(self, rev, name=None):
40 def __init__(self, rev, name=None):
41 if not name:
41 if not name:
42 fields = rev.split(':')
42 fields = rev.split(':')
43 if len(fields) == 2:
43 if len(fields) == 2:
44 self.rev, self.name = fields
44 self.rev, self.name = fields
45 else:
45 else:
46 self.rev, self.name = None, None
46 self.rev, self.name = None, None
47 else:
47 else:
48 self.rev, self.name = rev, name
48 self.rev, self.name = rev, name
49
49
50 def __str__(self):
50 def __str__(self):
51 return self.rev + ':' + self.name
51 return self.rev + ':' + self.name
52
52
53 class queue:
53 class queue:
54 def __init__(self, ui, path, patchdir=None):
54 def __init__(self, ui, path, patchdir=None):
55 self.basepath = path
55 self.basepath = path
56 self.path = patchdir or os.path.join(path, "patches")
56 self.path = patchdir or os.path.join(path, "patches")
57 self.opener = util.opener(self.path)
57 self.opener = util.opener(self.path)
58 self.ui = ui
58 self.ui = ui
59 self.applied = []
59 self.applied = []
60 self.full_series = []
60 self.full_series = []
61 self.applied_dirty = 0
61 self.applied_dirty = 0
62 self.series_dirty = 0
62 self.series_dirty = 0
63 self.series_path = "series"
63 self.series_path = "series"
64 self.status_path = "status"
64 self.status_path = "status"
65 self.guards_path = "guards"
65 self.guards_path = "guards"
66 self.active_guards = None
66 self.active_guards = None
67 self.guards_dirty = False
67 self.guards_dirty = False
68 self._diffopts = None
68 self._diffopts = None
69
69
70 if os.path.exists(self.join(self.series_path)):
70 if os.path.exists(self.join(self.series_path)):
71 self.full_series = self.opener(self.series_path).read().splitlines()
71 self.full_series = self.opener(self.series_path).read().splitlines()
72 self.parse_series()
72 self.parse_series()
73
73
74 if os.path.exists(self.join(self.status_path)):
74 if os.path.exists(self.join(self.status_path)):
75 lines = self.opener(self.status_path).read().splitlines()
75 lines = self.opener(self.status_path).read().splitlines()
76 self.applied = [statusentry(l) for l in lines]
76 self.applied = [statusentry(l) for l in lines]
77
77
78 def diffopts(self):
78 def diffopts(self):
79 if self._diffopts is None:
79 if self._diffopts is None:
80 self._diffopts = patch.diffopts(self.ui)
80 self._diffopts = patch.diffopts(self.ui)
81 return self._diffopts
81 return self._diffopts
82
82
83 def join(self, *p):
83 def join(self, *p):
84 return os.path.join(self.path, *p)
84 return os.path.join(self.path, *p)
85
85
86 def find_series(self, patch):
86 def find_series(self, patch):
87 pre = re.compile("(\s*)([^#]+)")
87 pre = re.compile("(\s*)([^#]+)")
88 index = 0
88 index = 0
89 for l in self.full_series:
89 for l in self.full_series:
90 m = pre.match(l)
90 m = pre.match(l)
91 if m:
91 if m:
92 s = m.group(2)
92 s = m.group(2)
93 s = s.rstrip()
93 s = s.rstrip()
94 if s == patch:
94 if s == patch:
95 return index
95 return index
96 index += 1
96 index += 1
97 return None
97 return None
98
98
99 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
99 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
100
100
101 def parse_series(self):
101 def parse_series(self):
102 self.series = []
102 self.series = []
103 self.series_guards = []
103 self.series_guards = []
104 for l in self.full_series:
104 for l in self.full_series:
105 h = l.find('#')
105 h = l.find('#')
106 if h == -1:
106 if h == -1:
107 patch = l
107 patch = l
108 comment = ''
108 comment = ''
109 elif h == 0:
109 elif h == 0:
110 continue
110 continue
111 else:
111 else:
112 patch = l[:h]
112 patch = l[:h]
113 comment = l[h:]
113 comment = l[h:]
114 patch = patch.strip()
114 patch = patch.strip()
115 if patch:
115 if patch:
116 self.series.append(patch)
116 self.series.append(patch)
117 self.series_guards.append(self.guard_re.findall(comment))
117 self.series_guards.append(self.guard_re.findall(comment))
118
118
119 def check_guard(self, guard):
119 def check_guard(self, guard):
120 bad_chars = '# \t\r\n\f'
120 bad_chars = '# \t\r\n\f'
121 first = guard[0]
121 first = guard[0]
122 for c in '-+':
122 for c in '-+':
123 if first == c:
123 if first == c:
124 return (_('guard %r starts with invalid character: %r') %
124 return (_('guard %r starts with invalid character: %r') %
125 (guard, c))
125 (guard, c))
126 for c in bad_chars:
126 for c in bad_chars:
127 if c in guard:
127 if c in guard:
128 return _('invalid character in guard %r: %r') % (guard, c)
128 return _('invalid character in guard %r: %r') % (guard, c)
129
129
130 def set_active(self, guards):
130 def set_active(self, guards):
131 for guard in guards:
131 for guard in guards:
132 bad = self.check_guard(guard)
132 bad = self.check_guard(guard)
133 if bad:
133 if bad:
134 raise util.Abort(bad)
134 raise util.Abort(bad)
135 guards = dict.fromkeys(guards).keys()
135 guards = dict.fromkeys(guards).keys()
136 guards.sort()
136 guards.sort()
137 self.ui.debug('active guards: %s\n' % ' '.join(guards))
137 self.ui.debug('active guards: %s\n' % ' '.join(guards))
138 self.active_guards = guards
138 self.active_guards = guards
139 self.guards_dirty = True
139 self.guards_dirty = True
140
140
141 def active(self):
141 def active(self):
142 if self.active_guards is None:
142 if self.active_guards is None:
143 self.active_guards = []
143 self.active_guards = []
144 try:
144 try:
145 guards = self.opener(self.guards_path).read().split()
145 guards = self.opener(self.guards_path).read().split()
146 except IOError, err:
146 except IOError, err:
147 if err.errno != errno.ENOENT: raise
147 if err.errno != errno.ENOENT: raise
148 guards = []
148 guards = []
149 for i, guard in enumerate(guards):
149 for i, guard in enumerate(guards):
150 bad = self.check_guard(guard)
150 bad = self.check_guard(guard)
151 if bad:
151 if bad:
152 self.ui.warn('%s:%d: %s\n' %
152 self.ui.warn('%s:%d: %s\n' %
153 (self.join(self.guards_path), i + 1, bad))
153 (self.join(self.guards_path), i + 1, bad))
154 else:
154 else:
155 self.active_guards.append(guard)
155 self.active_guards.append(guard)
156 return self.active_guards
156 return self.active_guards
157
157
158 def set_guards(self, idx, guards):
158 def set_guards(self, idx, guards):
159 for g in guards:
159 for g in guards:
160 if len(g) < 2:
160 if len(g) < 2:
161 raise util.Abort(_('guard %r too short') % g)
161 raise util.Abort(_('guard %r too short') % g)
162 if g[0] not in '-+':
162 if g[0] not in '-+':
163 raise util.Abort(_('guard %r starts with invalid char') % g)
163 raise util.Abort(_('guard %r starts with invalid char') % g)
164 bad = self.check_guard(g[1:])
164 bad = self.check_guard(g[1:])
165 if bad:
165 if bad:
166 raise util.Abort(bad)
166 raise util.Abort(bad)
167 drop = self.guard_re.sub('', self.full_series[idx])
167 drop = self.guard_re.sub('', self.full_series[idx])
168 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
168 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
169 self.parse_series()
169 self.parse_series()
170 self.series_dirty = True
170 self.series_dirty = True
171
171
172 def pushable(self, idx):
172 def pushable(self, idx):
173 if isinstance(idx, str):
173 if isinstance(idx, str):
174 idx = self.series.index(idx)
174 idx = self.series.index(idx)
175 patchguards = self.series_guards[idx]
175 patchguards = self.series_guards[idx]
176 if not patchguards:
176 if not patchguards:
177 return True, None
177 return True, None
178 default = False
178 default = False
179 guards = self.active()
179 guards = self.active()
180 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
180 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
181 if exactneg:
181 if exactneg:
182 return False, exactneg[0]
182 return False, exactneg[0]
183 pos = [g for g in patchguards if g[0] == '+']
183 pos = [g for g in patchguards if g[0] == '+']
184 exactpos = [g for g in pos if g[1:] in guards]
184 exactpos = [g for g in pos if g[1:] in guards]
185 if pos:
185 if pos:
186 if exactpos:
186 if exactpos:
187 return True, exactpos[0]
187 return True, exactpos[0]
188 return False, pos
188 return False, pos
189 return True, ''
189 return True, ''
190
190
191 def explain_pushable(self, idx, all_patches=False):
191 def explain_pushable(self, idx, all_patches=False):
192 write = all_patches and self.ui.write or self.ui.warn
192 write = all_patches and self.ui.write or self.ui.warn
193 if all_patches or self.ui.verbose:
193 if all_patches or self.ui.verbose:
194 if isinstance(idx, str):
194 if isinstance(idx, str):
195 idx = self.series.index(idx)
195 idx = self.series.index(idx)
196 pushable, why = self.pushable(idx)
196 pushable, why = self.pushable(idx)
197 if all_patches and pushable:
197 if all_patches and pushable:
198 if why is None:
198 if why is None:
199 write(_('allowing %s - no guards in effect\n') %
199 write(_('allowing %s - no guards in effect\n') %
200 self.series[idx])
200 self.series[idx])
201 else:
201 else:
202 if not why:
202 if not why:
203 write(_('allowing %s - no matching negative guards\n') %
203 write(_('allowing %s - no matching negative guards\n') %
204 self.series[idx])
204 self.series[idx])
205 else:
205 else:
206 write(_('allowing %s - guarded by %r\n') %
206 write(_('allowing %s - guarded by %r\n') %
207 (self.series[idx], why))
207 (self.series[idx], why))
208 if not pushable:
208 if not pushable:
209 if why:
209 if why:
210 write(_('skipping %s - guarded by %r\n') %
210 write(_('skipping %s - guarded by %r\n') %
211 (self.series[idx], ' '.join(why)))
211 (self.series[idx], ' '.join(why)))
212 else:
212 else:
213 write(_('skipping %s - no matching guards\n') %
213 write(_('skipping %s - no matching guards\n') %
214 self.series[idx])
214 self.series[idx])
215
215
216 def save_dirty(self):
216 def save_dirty(self):
217 def write_list(items, path):
217 def write_list(items, path):
218 fp = self.opener(path, 'w')
218 fp = self.opener(path, 'w')
219 for i in items:
219 for i in items:
220 print >> fp, i
220 print >> fp, i
221 fp.close()
221 fp.close()
222 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
222 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
223 if self.series_dirty: write_list(self.full_series, self.series_path)
223 if self.series_dirty: write_list(self.full_series, self.series_path)
224 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
224 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
225
225
226 def readheaders(self, patch):
226 def readheaders(self, patch):
227 def eatdiff(lines):
227 def eatdiff(lines):
228 while lines:
228 while lines:
229 l = lines[-1]
229 l = lines[-1]
230 if (l.startswith("diff -") or
230 if (l.startswith("diff -") or
231 l.startswith("Index:") or
231 l.startswith("Index:") or
232 l.startswith("===========")):
232 l.startswith("===========")):
233 del lines[-1]
233 del lines[-1]
234 else:
234 else:
235 break
235 break
236 def eatempty(lines):
236 def eatempty(lines):
237 while lines:
237 while lines:
238 l = lines[-1]
238 l = lines[-1]
239 if re.match('\s*$', l):
239 if re.match('\s*$', l):
240 del lines[-1]
240 del lines[-1]
241 else:
241 else:
242 break
242 break
243
243
244 pf = self.join(patch)
244 pf = self.join(patch)
245 message = []
245 message = []
246 comments = []
246 comments = []
247 user = None
247 user = None
248 date = None
248 date = None
249 format = None
249 format = None
250 subject = None
250 subject = None
251 diffstart = 0
251 diffstart = 0
252
252
253 for line in file(pf):
253 for line in file(pf):
254 line = line.rstrip()
254 line = line.rstrip()
255 if line.startswith('diff --git'):
256 diffstart = 2
257 break
255 if diffstart:
258 if diffstart:
256 if line.startswith('+++ '):
259 if line.startswith('+++ '):
257 diffstart = 2
260 diffstart = 2
258 break
261 break
259 if line.startswith("--- "):
262 if line.startswith("--- "):
260 diffstart = 1
263 diffstart = 1
261 continue
264 continue
262 elif format == "hgpatch":
265 elif format == "hgpatch":
263 # parse values when importing the result of an hg export
266 # parse values when importing the result of an hg export
264 if line.startswith("# User "):
267 if line.startswith("# User "):
265 user = line[7:]
268 user = line[7:]
266 elif line.startswith("# Date "):
269 elif line.startswith("# Date "):
267 date = line[7:]
270 date = line[7:]
268 elif not line.startswith("# ") and line:
271 elif not line.startswith("# ") and line:
269 message.append(line)
272 message.append(line)
270 format = None
273 format = None
271 elif line == '# HG changeset patch':
274 elif line == '# HG changeset patch':
272 format = "hgpatch"
275 format = "hgpatch"
273 elif (format != "tagdone" and (line.startswith("Subject: ") or
276 elif (format != "tagdone" and (line.startswith("Subject: ") or
274 line.startswith("subject: "))):
277 line.startswith("subject: "))):
275 subject = line[9:]
278 subject = line[9:]
276 format = "tag"
279 format = "tag"
277 elif (format != "tagdone" and (line.startswith("From: ") or
280 elif (format != "tagdone" and (line.startswith("From: ") or
278 line.startswith("from: "))):
281 line.startswith("from: "))):
279 user = line[6:]
282 user = line[6:]
280 format = "tag"
283 format = "tag"
281 elif format == "tag" and line == "":
284 elif format == "tag" and line == "":
282 # when looking for tags (subject: from: etc) they
285 # when looking for tags (subject: from: etc) they
283 # end once you find a blank line in the source
286 # end once you find a blank line in the source
284 format = "tagdone"
287 format = "tagdone"
285 elif message or line:
288 elif message or line:
286 message.append(line)
289 message.append(line)
287 comments.append(line)
290 comments.append(line)
288
291
289 eatdiff(message)
292 eatdiff(message)
290 eatdiff(comments)
293 eatdiff(comments)
291 eatempty(message)
294 eatempty(message)
292 eatempty(comments)
295 eatempty(comments)
293
296
294 # make sure message isn't empty
297 # make sure message isn't empty
295 if format and format.startswith("tag") and subject:
298 if format and format.startswith("tag") and subject:
296 message.insert(0, "")
299 message.insert(0, "")
297 message.insert(0, subject)
300 message.insert(0, subject)
298 return (message, comments, user, date, diffstart > 1)
301 return (message, comments, user, date, diffstart > 1)
299
302
300 def printdiff(self, repo, node1, node2=None, files=None,
303 def printdiff(self, repo, node1, node2=None, files=None,
301 fp=None, changes=None, opts=None):
304 fp=None, changes=None, opts={}):
302 patch.diff(repo, node1, node2, files,
305 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
306
307 patch.diff(repo, node1, node2, fns, match=matchfn,
303 fp=fp, changes=changes, opts=self.diffopts())
308 fp=fp, changes=changes, opts=self.diffopts())
304
309
305 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
310 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
306 # first try just applying the patch
311 # first try just applying the patch
307 (err, n) = self.apply(repo, [ patch ], update_status=False,
312 (err, n) = self.apply(repo, [ patch ], update_status=False,
308 strict=True, merge=rev, wlock=wlock)
313 strict=True, merge=rev, wlock=wlock)
309
314
310 if err == 0:
315 if err == 0:
311 return (err, n)
316 return (err, n)
312
317
313 if n is None:
318 if n is None:
314 raise util.Abort(_("apply failed for patch %s") % patch)
319 raise util.Abort(_("apply failed for patch %s") % patch)
315
320
316 self.ui.warn("patch didn't work out, merging %s\n" % patch)
321 self.ui.warn("patch didn't work out, merging %s\n" % patch)
317
322
318 # apply failed, strip away that rev and merge.
323 # apply failed, strip away that rev and merge.
319 hg.clean(repo, head, wlock=wlock)
324 hg.clean(repo, head, wlock=wlock)
320 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
325 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
321
326
322 c = repo.changelog.read(rev)
327 c = repo.changelog.read(rev)
323 ret = hg.merge(repo, rev, wlock=wlock)
328 ret = hg.merge(repo, rev, wlock=wlock)
324 if ret:
329 if ret:
325 raise util.Abort(_("update returned %d") % ret)
330 raise util.Abort(_("update returned %d") % ret)
326 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
331 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
327 if n == None:
332 if n == None:
328 raise util.Abort(_("repo commit failed"))
333 raise util.Abort(_("repo commit failed"))
329 try:
334 try:
330 message, comments, user, date, patchfound = mergeq.readheaders(patch)
335 message, comments, user, date, patchfound = mergeq.readheaders(patch)
331 except:
336 except:
332 raise util.Abort(_("unable to read %s") % patch)
337 raise util.Abort(_("unable to read %s") % patch)
333
338
334 patchf = self.opener(patch, "w")
339 patchf = self.opener(patch, "w")
335 if comments:
340 if comments:
336 comments = "\n".join(comments) + '\n\n'
341 comments = "\n".join(comments) + '\n\n'
337 patchf.write(comments)
342 patchf.write(comments)
338 self.printdiff(repo, head, n, fp=patchf)
343 self.printdiff(repo, head, n, fp=patchf)
339 patchf.close()
344 patchf.close()
340 return (0, n)
345 return (0, n)
341
346
342 def qparents(self, repo, rev=None):
347 def qparents(self, repo, rev=None):
343 if rev is None:
348 if rev is None:
344 (p1, p2) = repo.dirstate.parents()
349 (p1, p2) = repo.dirstate.parents()
345 if p2 == revlog.nullid:
350 if p2 == revlog.nullid:
346 return p1
351 return p1
347 if len(self.applied) == 0:
352 if len(self.applied) == 0:
348 return None
353 return None
349 return revlog.bin(self.applied[-1].rev)
354 return revlog.bin(self.applied[-1].rev)
350 pp = repo.changelog.parents(rev)
355 pp = repo.changelog.parents(rev)
351 if pp[1] != revlog.nullid:
356 if pp[1] != revlog.nullid:
352 arevs = [ x.rev for x in self.applied ]
357 arevs = [ x.rev for x in self.applied ]
353 p0 = revlog.hex(pp[0])
358 p0 = revlog.hex(pp[0])
354 p1 = revlog.hex(pp[1])
359 p1 = revlog.hex(pp[1])
355 if p0 in arevs:
360 if p0 in arevs:
356 return pp[0]
361 return pp[0]
357 if p1 in arevs:
362 if p1 in arevs:
358 return pp[1]
363 return pp[1]
359 return pp[0]
364 return pp[0]
360
365
361 def mergepatch(self, repo, mergeq, series, wlock):
366 def mergepatch(self, repo, mergeq, series, wlock):
362 if len(self.applied) == 0:
367 if len(self.applied) == 0:
363 # each of the patches merged in will have two parents. This
368 # each of the patches merged in will have two parents. This
364 # can confuse the qrefresh, qdiff, and strip code because it
369 # can confuse the qrefresh, qdiff, and strip code because it
365 # needs to know which parent is actually in the patch queue.
370 # needs to know which parent is actually in the patch queue.
366 # so, we insert a merge marker with only one parent. This way
371 # so, we insert a merge marker with only one parent. This way
367 # the first patch in the queue is never a merge patch
372 # the first patch in the queue is never a merge patch
368 #
373 #
369 pname = ".hg.patches.merge.marker"
374 pname = ".hg.patches.merge.marker"
370 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
375 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
371 wlock=wlock)
376 wlock=wlock)
372 self.applied.append(statusentry(revlog.hex(n), pname))
377 self.applied.append(statusentry(revlog.hex(n), pname))
373 self.applied_dirty = 1
378 self.applied_dirty = 1
374
379
375 head = self.qparents(repo)
380 head = self.qparents(repo)
376
381
377 for patch in series:
382 for patch in series:
378 patch = mergeq.lookup(patch, strict=True)
383 patch = mergeq.lookup(patch, strict=True)
379 if not patch:
384 if not patch:
380 self.ui.warn("patch %s does not exist\n" % patch)
385 self.ui.warn("patch %s does not exist\n" % patch)
381 return (1, None)
386 return (1, None)
382 pushable, reason = self.pushable(patch)
387 pushable, reason = self.pushable(patch)
383 if not pushable:
388 if not pushable:
384 self.explain_pushable(patch, all_patches=True)
389 self.explain_pushable(patch, all_patches=True)
385 continue
390 continue
386 info = mergeq.isapplied(patch)
391 info = mergeq.isapplied(patch)
387 if not info:
392 if not info:
388 self.ui.warn("patch %s is not applied\n" % patch)
393 self.ui.warn("patch %s is not applied\n" % patch)
389 return (1, None)
394 return (1, None)
390 rev = revlog.bin(info[1])
395 rev = revlog.bin(info[1])
391 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
396 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
392 if head:
397 if head:
393 self.applied.append(statusentry(revlog.hex(head), patch))
398 self.applied.append(statusentry(revlog.hex(head), patch))
394 self.applied_dirty = 1
399 self.applied_dirty = 1
395 if err:
400 if err:
396 return (err, head)
401 return (err, head)
397 return (0, head)
402 return (0, head)
398
403
399 def patch(self, repo, patchfile):
404 def patch(self, repo, patchfile):
400 '''Apply patchfile to the working directory.
405 '''Apply patchfile to the working directory.
401 patchfile: file name of patch'''
406 patchfile: file name of patch'''
402 try:
407 try:
403 (files, fuzz) = patch.patch(patchfile, self.ui, strip=1,
408 (files, fuzz) = patch.patch(patchfile, self.ui, strip=1,
404 cwd=repo.root)
409 cwd=repo.root)
405 except Exception, inst:
410 except Exception, inst:
406 self.ui.note(str(inst) + '\n')
411 self.ui.note(str(inst) + '\n')
407 if not self.ui.verbose:
412 if not self.ui.verbose:
408 self.ui.warn("patch failed, unable to continue (try -v)\n")
413 self.ui.warn("patch failed, unable to continue (try -v)\n")
409 return (False, [], False)
414 return (False, [], False)
410
415
411 return (True, files.keys(), fuzz)
416 return (True, files, fuzz)
412
417
413 def apply(self, repo, series, list=False, update_status=True,
418 def apply(self, repo, series, list=False, update_status=True,
414 strict=False, patchdir=None, merge=None, wlock=None):
419 strict=False, patchdir=None, merge=None, wlock=None):
415 # TODO unify with commands.py
420 # TODO unify with commands.py
416 if not patchdir:
421 if not patchdir:
417 patchdir = self.path
422 patchdir = self.path
418 err = 0
423 err = 0
419 if not wlock:
424 if not wlock:
420 wlock = repo.wlock()
425 wlock = repo.wlock()
421 lock = repo.lock()
426 lock = repo.lock()
422 tr = repo.transaction()
427 tr = repo.transaction()
423 n = None
428 n = None
424 for patch in series:
429 for patchname in series:
425 pushable, reason = self.pushable(patch)
430 pushable, reason = self.pushable(patchname)
426 if not pushable:
431 if not pushable:
427 self.explain_pushable(patch, all_patches=True)
432 self.explain_pushable(patchname, all_patches=True)
428 continue
433 continue
429 self.ui.warn("applying %s\n" % patch)
434 self.ui.warn("applying %s\n" % patchname)
430 pf = os.path.join(patchdir, patch)
435 pf = os.path.join(patchdir, patchname)
431
436
432 try:
437 try:
433 message, comments, user, date, patchfound = self.readheaders(patch)
438 message, comments, user, date, patchfound = self.readheaders(patchname)
434 except:
439 except:
435 self.ui.warn("Unable to read %s\n" % pf)
440 self.ui.warn("Unable to read %s\n" % patchname)
436 err = 1
441 err = 1
437 break
442 break
438
443
439 if not message:
444 if not message:
440 message = "imported patch %s\n" % patch
445 message = "imported patch %s\n" % patchname
441 else:
446 else:
442 if list:
447 if list:
443 message.append("\nimported patch %s" % patch)
448 message.append("\nimported patch %s" % patchname)
444 message = '\n'.join(message)
449 message = '\n'.join(message)
445
450
446 (patcherr, files, fuzz) = self.patch(repo, pf)
451 (patcherr, files, fuzz) = self.patch(repo, pf)
447 patcherr = not patcherr
452 patcherr = not patcherr
448
453
449 if merge and len(files) > 0:
454 if merge and files:
450 # Mark as merged and update dirstate parent info
455 # Mark as merged and update dirstate parent info
451 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
456 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
452 p1, p2 = repo.dirstate.parents()
457 p1, p2 = repo.dirstate.parents()
453 repo.dirstate.setparents(p1, merge)
458 repo.dirstate.setparents(p1, merge)
454 if len(files) > 0:
459 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
455 cwd = repo.getcwd()
456 cfiles = files
457 if cwd:
458 cfiles = [util.pathto(cwd, f) for f in files]
459 cmdutil.addremove(repo, cfiles, wlock=wlock)
460 n = repo.commit(files, message, user, date, force=1, lock=lock,
460 n = repo.commit(files, message, user, date, force=1, lock=lock,
461 wlock=wlock)
461 wlock=wlock)
462
462
463 if n == None:
463 if n == None:
464 raise util.Abort(_("repo commit failed"))
464 raise util.Abort(_("repo commit failed"))
465
465
466 if update_status:
466 if update_status:
467 self.applied.append(statusentry(revlog.hex(n), patch))
467 self.applied.append(statusentry(revlog.hex(n), patchname))
468
468
469 if patcherr:
469 if patcherr:
470 if not patchfound:
470 if not patchfound:
471 self.ui.warn("patch %s is empty\n" % patch)
471 self.ui.warn("patch %s is empty\n" % patchname)
472 err = 0
472 err = 0
473 else:
473 else:
474 self.ui.warn("patch failed, rejects left in working dir\n")
474 self.ui.warn("patch failed, rejects left in working dir\n")
475 err = 1
475 err = 1
476 break
476 break
477
477
478 if fuzz and strict:
478 if fuzz and strict:
479 self.ui.warn("fuzz found when applying patch, stopping\n")
479 self.ui.warn("fuzz found when applying patch, stopping\n")
480 err = 1
480 err = 1
481 break
481 break
482 tr.close()
482 tr.close()
483 return (err, n)
483 return (err, n)
484
484
485 def delete(self, repo, patches, keep=False):
485 def delete(self, repo, patches, keep=False):
486 realpatches = []
486 realpatches = []
487 for patch in patches:
487 for patch in patches:
488 patch = self.lookup(patch, strict=True)
488 patch = self.lookup(patch, strict=True)
489 info = self.isapplied(patch)
489 info = self.isapplied(patch)
490 if info:
490 if info:
491 raise util.Abort(_("cannot delete applied patch %s") % patch)
491 raise util.Abort(_("cannot delete applied patch %s") % patch)
492 if patch not in self.series:
492 if patch not in self.series:
493 raise util.Abort(_("patch %s not in series file") % patch)
493 raise util.Abort(_("patch %s not in series file") % patch)
494 realpatches.append(patch)
494 realpatches.append(patch)
495
495
496 if not keep:
496 if not keep:
497 r = self.qrepo()
497 r = self.qrepo()
498 if r:
498 if r:
499 r.remove(realpatches, True)
499 r.remove(realpatches, True)
500 else:
500 else:
501 os.unlink(self.join(patch))
501 os.unlink(self.join(patch))
502
502
503 indices = [self.find_series(p) for p in realpatches]
503 indices = [self.find_series(p) for p in realpatches]
504 indices.sort()
504 indices.sort()
505 for i in indices[-1::-1]:
505 for i in indices[-1::-1]:
506 del self.full_series[i]
506 del self.full_series[i]
507 self.parse_series()
507 self.parse_series()
508 self.series_dirty = 1
508 self.series_dirty = 1
509
509
510 def check_toppatch(self, repo):
510 def check_toppatch(self, repo):
511 if len(self.applied) > 0:
511 if len(self.applied) > 0:
512 top = revlog.bin(self.applied[-1].rev)
512 top = revlog.bin(self.applied[-1].rev)
513 pp = repo.dirstate.parents()
513 pp = repo.dirstate.parents()
514 if top not in pp:
514 if top not in pp:
515 raise util.Abort(_("queue top not at same revision as working directory"))
515 raise util.Abort(_("queue top not at same revision as working directory"))
516 return top
516 return top
517 return None
517 return None
518 def check_localchanges(self, repo, force=False, refresh=True):
518 def check_localchanges(self, repo, force=False, refresh=True):
519 m, a, r, d = repo.status()[:4]
519 m, a, r, d = repo.status()[:4]
520 if m or a or r or d:
520 if m or a or r or d:
521 if not force:
521 if not force:
522 if refresh:
522 if refresh:
523 raise util.Abort(_("local changes found, refresh first"))
523 raise util.Abort(_("local changes found, refresh first"))
524 else:
524 else:
525 raise util.Abort(_("local changes found"))
525 raise util.Abort(_("local changes found"))
526 return m, a, r, d
526 return m, a, r, d
527 def new(self, repo, patch, msg=None, force=None):
527 def new(self, repo, patch, msg=None, force=None):
528 if os.path.exists(self.join(patch)):
528 if os.path.exists(self.join(patch)):
529 raise util.Abort(_('patch "%s" already exists') % patch)
529 raise util.Abort(_('patch "%s" already exists') % patch)
530 m, a, r, d = self.check_localchanges(repo, force)
530 m, a, r, d = self.check_localchanges(repo, force)
531 commitfiles = m + a + r
531 commitfiles = m + a + r
532 self.check_toppatch(repo)
532 self.check_toppatch(repo)
533 wlock = repo.wlock()
533 wlock = repo.wlock()
534 insert = self.full_series_end()
534 insert = self.full_series_end()
535 if msg:
535 if msg:
536 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
536 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
537 wlock=wlock)
537 wlock=wlock)
538 else:
538 else:
539 n = repo.commit(commitfiles,
539 n = repo.commit(commitfiles,
540 "New patch: %s" % patch, force=True, wlock=wlock)
540 "New patch: %s" % patch, force=True, wlock=wlock)
541 if n == None:
541 if n == None:
542 raise util.Abort(_("repo commit failed"))
542 raise util.Abort(_("repo commit failed"))
543 self.full_series[insert:insert] = [patch]
543 self.full_series[insert:insert] = [patch]
544 self.applied.append(statusentry(revlog.hex(n), patch))
544 self.applied.append(statusentry(revlog.hex(n), patch))
545 self.parse_series()
545 self.parse_series()
546 self.series_dirty = 1
546 self.series_dirty = 1
547 self.applied_dirty = 1
547 self.applied_dirty = 1
548 p = self.opener(patch, "w")
548 p = self.opener(patch, "w")
549 if msg:
549 if msg:
550 msg = msg + "\n"
550 msg = msg + "\n"
551 p.write(msg)
551 p.write(msg)
552 p.close()
552 p.close()
553 wlock = None
553 wlock = None
554 r = self.qrepo()
554 r = self.qrepo()
555 if r: r.add([patch])
555 if r: r.add([patch])
556 if commitfiles:
556 if commitfiles:
557 self.refresh(repo, short=True)
557 self.refresh(repo, short=True)
558
558
559 def strip(self, repo, rev, update=True, backup="all", wlock=None):
559 def strip(self, repo, rev, update=True, backup="all", wlock=None):
560 def limitheads(chlog, stop):
560 def limitheads(chlog, stop):
561 """return the list of all nodes that have no children"""
561 """return the list of all nodes that have no children"""
562 p = {}
562 p = {}
563 h = []
563 h = []
564 stoprev = 0
564 stoprev = 0
565 if stop in chlog.nodemap:
565 if stop in chlog.nodemap:
566 stoprev = chlog.rev(stop)
566 stoprev = chlog.rev(stop)
567
567
568 for r in range(chlog.count() - 1, -1, -1):
568 for r in range(chlog.count() - 1, -1, -1):
569 n = chlog.node(r)
569 n = chlog.node(r)
570 if n not in p:
570 if n not in p:
571 h.append(n)
571 h.append(n)
572 if n == stop:
572 if n == stop:
573 break
573 break
574 if r < stoprev:
574 if r < stoprev:
575 break
575 break
576 for pn in chlog.parents(n):
576 for pn in chlog.parents(n):
577 p[pn] = 1
577 p[pn] = 1
578 return h
578 return h
579
579
580 def bundle(cg):
580 def bundle(cg):
581 backupdir = repo.join("strip-backup")
581 backupdir = repo.join("strip-backup")
582 if not os.path.isdir(backupdir):
582 if not os.path.isdir(backupdir):
583 os.mkdir(backupdir)
583 os.mkdir(backupdir)
584 name = os.path.join(backupdir, "%s" % revlog.short(rev))
584 name = os.path.join(backupdir, "%s" % revlog.short(rev))
585 name = savename(name)
585 name = savename(name)
586 self.ui.warn("saving bundle to %s\n" % name)
586 self.ui.warn("saving bundle to %s\n" % name)
587 # TODO, exclusive open
587 # TODO, exclusive open
588 f = open(name, "wb")
588 f = open(name, "wb")
589 try:
589 try:
590 f.write("HG10")
590 f.write("HG10")
591 z = bz2.BZ2Compressor(9)
591 z = bz2.BZ2Compressor(9)
592 while 1:
592 while 1:
593 chunk = cg.read(4096)
593 chunk = cg.read(4096)
594 if not chunk:
594 if not chunk:
595 break
595 break
596 f.write(z.compress(chunk))
596 f.write(z.compress(chunk))
597 f.write(z.flush())
597 f.write(z.flush())
598 except:
598 except:
599 os.unlink(name)
599 os.unlink(name)
600 raise
600 raise
601 f.close()
601 f.close()
602 return name
602 return name
603
603
604 def stripall(rev, revnum):
604 def stripall(rev, revnum):
605 cl = repo.changelog
605 cl = repo.changelog
606 c = cl.read(rev)
606 c = cl.read(rev)
607 mm = repo.manifest.read(c[0])
607 mm = repo.manifest.read(c[0])
608 seen = {}
608 seen = {}
609
609
610 for x in xrange(revnum, cl.count()):
610 for x in xrange(revnum, cl.count()):
611 c = cl.read(cl.node(x))
611 c = cl.read(cl.node(x))
612 for f in c[3]:
612 for f in c[3]:
613 if f in seen:
613 if f in seen:
614 continue
614 continue
615 seen[f] = 1
615 seen[f] = 1
616 if f in mm:
616 if f in mm:
617 filerev = mm[f]
617 filerev = mm[f]
618 else:
618 else:
619 filerev = 0
619 filerev = 0
620 seen[f] = filerev
620 seen[f] = filerev
621 # we go in two steps here so the strip loop happens in a
621 # we go in two steps here so the strip loop happens in a
622 # sensible order. When stripping many files, this helps keep
622 # sensible order. When stripping many files, this helps keep
623 # our disk access patterns under control.
623 # our disk access patterns under control.
624 seen_list = seen.keys()
624 seen_list = seen.keys()
625 seen_list.sort()
625 seen_list.sort()
626 for f in seen_list:
626 for f in seen_list:
627 ff = repo.file(f)
627 ff = repo.file(f)
628 filerev = seen[f]
628 filerev = seen[f]
629 if filerev != 0:
629 if filerev != 0:
630 if filerev in ff.nodemap:
630 if filerev in ff.nodemap:
631 filerev = ff.rev(filerev)
631 filerev = ff.rev(filerev)
632 else:
632 else:
633 filerev = 0
633 filerev = 0
634 ff.strip(filerev, revnum)
634 ff.strip(filerev, revnum)
635
635
636 if not wlock:
636 if not wlock:
637 wlock = repo.wlock()
637 wlock = repo.wlock()
638 lock = repo.lock()
638 lock = repo.lock()
639 chlog = repo.changelog
639 chlog = repo.changelog
640 # TODO delete the undo files, and handle undo of merge sets
640 # TODO delete the undo files, and handle undo of merge sets
641 pp = chlog.parents(rev)
641 pp = chlog.parents(rev)
642 revnum = chlog.rev(rev)
642 revnum = chlog.rev(rev)
643
643
644 if update:
644 if update:
645 self.check_localchanges(repo, refresh=False)
645 self.check_localchanges(repo, refresh=False)
646 urev = self.qparents(repo, rev)
646 urev = self.qparents(repo, rev)
647 hg.clean(repo, urev, wlock=wlock)
647 hg.clean(repo, urev, wlock=wlock)
648 repo.dirstate.write()
648 repo.dirstate.write()
649
649
650 # save is a list of all the branches we are truncating away
650 # save is a list of all the branches we are truncating away
651 # that we actually want to keep. changegroup will be used
651 # that we actually want to keep. changegroup will be used
652 # to preserve them and add them back after the truncate
652 # to preserve them and add them back after the truncate
653 saveheads = []
653 saveheads = []
654 savebases = {}
654 savebases = {}
655
655
656 heads = limitheads(chlog, rev)
656 heads = limitheads(chlog, rev)
657 seen = {}
657 seen = {}
658
658
659 # search through all the heads, finding those where the revision
659 # search through all the heads, finding those where the revision
660 # we want to strip away is an ancestor. Also look for merges
660 # we want to strip away is an ancestor. Also look for merges
661 # that might be turned into new heads by the strip.
661 # that might be turned into new heads by the strip.
662 while heads:
662 while heads:
663 h = heads.pop()
663 h = heads.pop()
664 n = h
664 n = h
665 while True:
665 while True:
666 seen[n] = 1
666 seen[n] = 1
667 pp = chlog.parents(n)
667 pp = chlog.parents(n)
668 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
668 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
669 if pp[1] not in seen:
669 if pp[1] not in seen:
670 heads.append(pp[1])
670 heads.append(pp[1])
671 if pp[0] == revlog.nullid:
671 if pp[0] == revlog.nullid:
672 break
672 break
673 if chlog.rev(pp[0]) < revnum:
673 if chlog.rev(pp[0]) < revnum:
674 break
674 break
675 n = pp[0]
675 n = pp[0]
676 if n == rev:
676 if n == rev:
677 break
677 break
678 r = chlog.reachable(h, rev)
678 r = chlog.reachable(h, rev)
679 if rev not in r:
679 if rev not in r:
680 saveheads.append(h)
680 saveheads.append(h)
681 for x in r:
681 for x in r:
682 if chlog.rev(x) > revnum:
682 if chlog.rev(x) > revnum:
683 savebases[x] = 1
683 savebases[x] = 1
684
684
685 # create a changegroup for all the branches we need to keep
685 # create a changegroup for all the branches we need to keep
686 if backup == "all":
686 if backup == "all":
687 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
687 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
688 bundle(backupch)
688 bundle(backupch)
689 if saveheads:
689 if saveheads:
690 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
690 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
691 chgrpfile = bundle(backupch)
691 chgrpfile = bundle(backupch)
692
692
693 stripall(rev, revnum)
693 stripall(rev, revnum)
694
694
695 change = chlog.read(rev)
695 change = chlog.read(rev)
696 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
696 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
697 chlog.strip(revnum, revnum)
697 chlog.strip(revnum, revnum)
698 if saveheads:
698 if saveheads:
699 self.ui.status("adding branch\n")
699 self.ui.status("adding branch\n")
700 commands.unbundle(self.ui, repo, chgrpfile, update=False)
700 commands.unbundle(self.ui, repo, chgrpfile, update=False)
701 if backup != "strip":
701 if backup != "strip":
702 os.unlink(chgrpfile)
702 os.unlink(chgrpfile)
703
703
704 def isapplied(self, patch):
704 def isapplied(self, patch):
705 """returns (index, rev, patch)"""
705 """returns (index, rev, patch)"""
706 for i in xrange(len(self.applied)):
706 for i in xrange(len(self.applied)):
707 a = self.applied[i]
707 a = self.applied[i]
708 if a.name == patch:
708 if a.name == patch:
709 return (i, a.rev, a.name)
709 return (i, a.rev, a.name)
710 return None
710 return None
711
711
712 # if the exact patch name does not exist, we try a few
712 # if the exact patch name does not exist, we try a few
713 # variations. If strict is passed, we try only #1
713 # variations. If strict is passed, we try only #1
714 #
714 #
715 # 1) a number to indicate an offset in the series file
715 # 1) a number to indicate an offset in the series file
716 # 2) a unique substring of the patch name was given
716 # 2) a unique substring of the patch name was given
717 # 3) patchname[-+]num to indicate an offset in the series file
717 # 3) patchname[-+]num to indicate an offset in the series file
718 def lookup(self, patch, strict=False):
718 def lookup(self, patch, strict=False):
719 patch = patch and str(patch)
719 patch = patch and str(patch)
720
720
721 def partial_name(s):
721 def partial_name(s):
722 if s in self.series:
722 if s in self.series:
723 return s
723 return s
724 matches = [x for x in self.series if s in x]
724 matches = [x for x in self.series if s in x]
725 if len(matches) > 1:
725 if len(matches) > 1:
726 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
726 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
727 for m in matches:
727 for m in matches:
728 self.ui.warn(' %s\n' % m)
728 self.ui.warn(' %s\n' % m)
729 return None
729 return None
730 if matches:
730 if matches:
731 return matches[0]
731 return matches[0]
732 if len(self.series) > 0 and len(self.applied) > 0:
732 if len(self.series) > 0 and len(self.applied) > 0:
733 if s == 'qtip':
733 if s == 'qtip':
734 return self.series[self.series_end()-1]
734 return self.series[self.series_end()-1]
735 if s == 'qbase':
735 if s == 'qbase':
736 return self.series[0]
736 return self.series[0]
737 return None
737 return None
738 if patch == None:
738 if patch == None:
739 return None
739 return None
740
740
741 # we don't want to return a partial match until we make
741 # we don't want to return a partial match until we make
742 # sure the file name passed in does not exist (checked below)
742 # sure the file name passed in does not exist (checked below)
743 res = partial_name(patch)
743 res = partial_name(patch)
744 if res and res == patch:
744 if res and res == patch:
745 return res
745 return res
746
746
747 if not os.path.isfile(self.join(patch)):
747 if not os.path.isfile(self.join(patch)):
748 try:
748 try:
749 sno = int(patch)
749 sno = int(patch)
750 except(ValueError, OverflowError):
750 except(ValueError, OverflowError):
751 pass
751 pass
752 else:
752 else:
753 if sno < len(self.series):
753 if sno < len(self.series):
754 return self.series[sno]
754 return self.series[sno]
755 if not strict:
755 if not strict:
756 # return any partial match made above
756 # return any partial match made above
757 if res:
757 if res:
758 return res
758 return res
759 minus = patch.rsplit('-', 1)
759 minus = patch.rsplit('-', 1)
760 if len(minus) > 1:
760 if len(minus) > 1:
761 res = partial_name(minus[0])
761 res = partial_name(minus[0])
762 if res:
762 if res:
763 i = self.series.index(res)
763 i = self.series.index(res)
764 try:
764 try:
765 off = int(minus[1] or 1)
765 off = int(minus[1] or 1)
766 except(ValueError, OverflowError):
766 except(ValueError, OverflowError):
767 pass
767 pass
768 else:
768 else:
769 if i - off >= 0:
769 if i - off >= 0:
770 return self.series[i - off]
770 return self.series[i - off]
771 plus = patch.rsplit('+', 1)
771 plus = patch.rsplit('+', 1)
772 if len(plus) > 1:
772 if len(plus) > 1:
773 res = partial_name(plus[0])
773 res = partial_name(plus[0])
774 if res:
774 if res:
775 i = self.series.index(res)
775 i = self.series.index(res)
776 try:
776 try:
777 off = int(plus[1] or 1)
777 off = int(plus[1] or 1)
778 except(ValueError, OverflowError):
778 except(ValueError, OverflowError):
779 pass
779 pass
780 else:
780 else:
781 if i + off < len(self.series):
781 if i + off < len(self.series):
782 return self.series[i + off]
782 return self.series[i + off]
783 raise util.Abort(_("patch %s not in series") % patch)
783 raise util.Abort(_("patch %s not in series") % patch)
784
784
785 def push(self, repo, patch=None, force=False, list=False,
785 def push(self, repo, patch=None, force=False, list=False,
786 mergeq=None, wlock=None):
786 mergeq=None, wlock=None):
787 if not wlock:
787 if not wlock:
788 wlock = repo.wlock()
788 wlock = repo.wlock()
789 patch = self.lookup(patch)
789 patch = self.lookup(patch)
790 if patch and self.isapplied(patch):
790 if patch and self.isapplied(patch):
791 self.ui.warn(_("patch %s is already applied\n") % patch)
791 self.ui.warn(_("patch %s is already applied\n") % patch)
792 sys.exit(1)
792 sys.exit(1)
793 if self.series_end() == len(self.series):
793 if self.series_end() == len(self.series):
794 self.ui.warn(_("patch series fully applied\n"))
794 self.ui.warn(_("patch series fully applied\n"))
795 sys.exit(1)
795 sys.exit(1)
796 if not force:
796 if not force:
797 self.check_localchanges(repo)
797 self.check_localchanges(repo)
798
798
799 self.applied_dirty = 1;
799 self.applied_dirty = 1;
800 start = self.series_end()
800 start = self.series_end()
801 if start > 0:
801 if start > 0:
802 self.check_toppatch(repo)
802 self.check_toppatch(repo)
803 if not patch:
803 if not patch:
804 patch = self.series[start]
804 patch = self.series[start]
805 end = start + 1
805 end = start + 1
806 else:
806 else:
807 end = self.series.index(patch, start) + 1
807 end = self.series.index(patch, start) + 1
808 s = self.series[start:end]
808 s = self.series[start:end]
809 if mergeq:
809 if mergeq:
810 ret = self.mergepatch(repo, mergeq, s, wlock)
810 ret = self.mergepatch(repo, mergeq, s, wlock)
811 else:
811 else:
812 ret = self.apply(repo, s, list, wlock=wlock)
812 ret = self.apply(repo, s, list, wlock=wlock)
813 top = self.applied[-1].name
813 top = self.applied[-1].name
814 if ret[0]:
814 if ret[0]:
815 self.ui.write("Errors during apply, please fix and refresh %s\n" %
815 self.ui.write("Errors during apply, please fix and refresh %s\n" %
816 top)
816 top)
817 else:
817 else:
818 self.ui.write("Now at: %s\n" % top)
818 self.ui.write("Now at: %s\n" % top)
819 return ret[0]
819 return ret[0]
820
820
821 def pop(self, repo, patch=None, force=False, update=True, all=False,
821 def pop(self, repo, patch=None, force=False, update=True, all=False,
822 wlock=None):
822 wlock=None):
823 def getfile(f, rev):
823 def getfile(f, rev):
824 t = repo.file(f).read(rev)
824 t = repo.file(f).read(rev)
825 try:
825 try:
826 repo.wfile(f, "w").write(t)
826 repo.wfile(f, "w").write(t)
827 except IOError:
827 except IOError:
828 try:
828 try:
829 os.makedirs(os.path.dirname(repo.wjoin(f)))
829 os.makedirs(os.path.dirname(repo.wjoin(f)))
830 except OSError, err:
830 except OSError, err:
831 if err.errno != errno.EEXIST: raise
831 if err.errno != errno.EEXIST: raise
832 repo.wfile(f, "w").write(t)
832 repo.wfile(f, "w").write(t)
833
833
834 if not wlock:
834 if not wlock:
835 wlock = repo.wlock()
835 wlock = repo.wlock()
836 if patch:
836 if patch:
837 # index, rev, patch
837 # index, rev, patch
838 info = self.isapplied(patch)
838 info = self.isapplied(patch)
839 if not info:
839 if not info:
840 patch = self.lookup(patch)
840 patch = self.lookup(patch)
841 info = self.isapplied(patch)
841 info = self.isapplied(patch)
842 if not info:
842 if not info:
843 raise util.Abort(_("patch %s is not applied") % patch)
843 raise util.Abort(_("patch %s is not applied") % patch)
844 if len(self.applied) == 0:
844 if len(self.applied) == 0:
845 self.ui.warn(_("no patches applied\n"))
845 self.ui.warn(_("no patches applied\n"))
846 sys.exit(1)
846 sys.exit(1)
847
847
848 if not update:
848 if not update:
849 parents = repo.dirstate.parents()
849 parents = repo.dirstate.parents()
850 rr = [ revlog.bin(x.rev) for x in self.applied ]
850 rr = [ revlog.bin(x.rev) for x in self.applied ]
851 for p in parents:
851 for p in parents:
852 if p in rr:
852 if p in rr:
853 self.ui.warn("qpop: forcing dirstate update\n")
853 self.ui.warn("qpop: forcing dirstate update\n")
854 update = True
854 update = True
855
855
856 if not force and update:
856 if not force and update:
857 self.check_localchanges(repo)
857 self.check_localchanges(repo)
858
858
859 self.applied_dirty = 1;
859 self.applied_dirty = 1;
860 end = len(self.applied)
860 end = len(self.applied)
861 if not patch:
861 if not patch:
862 if all:
862 if all:
863 popi = 0
863 popi = 0
864 else:
864 else:
865 popi = len(self.applied) - 1
865 popi = len(self.applied) - 1
866 else:
866 else:
867 popi = info[0] + 1
867 popi = info[0] + 1
868 if popi >= end:
868 if popi >= end:
869 self.ui.warn("qpop: %s is already at the top\n" % patch)
869 self.ui.warn("qpop: %s is already at the top\n" % patch)
870 return
870 return
871 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
871 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
872
872
873 start = info[0]
873 start = info[0]
874 rev = revlog.bin(info[1])
874 rev = revlog.bin(info[1])
875
875
876 # we know there are no local changes, so we can make a simplified
876 # we know there are no local changes, so we can make a simplified
877 # form of hg.update.
877 # form of hg.update.
878 if update:
878 if update:
879 top = self.check_toppatch(repo)
879 top = self.check_toppatch(repo)
880 qp = self.qparents(repo, rev)
880 qp = self.qparents(repo, rev)
881 changes = repo.changelog.read(qp)
881 changes = repo.changelog.read(qp)
882 mmap = repo.manifest.read(changes[0])
882 mmap = repo.manifest.read(changes[0])
883 m, a, r, d, u = repo.status(qp, top)[:5]
883 m, a, r, d, u = repo.status(qp, top)[:5]
884 if d:
884 if d:
885 raise util.Abort("deletions found between repo revs")
885 raise util.Abort("deletions found between repo revs")
886 for f in m:
886 for f in m:
887 getfile(f, mmap[f])
887 getfile(f, mmap[f])
888 for f in r:
888 for f in r:
889 getfile(f, mmap[f])
889 getfile(f, mmap[f])
890 util.set_exec(repo.wjoin(f), mmap.execf(f))
890 util.set_exec(repo.wjoin(f), mmap.execf(f))
891 repo.dirstate.update(m + r, 'n')
891 repo.dirstate.update(m + r, 'n')
892 for f in a:
892 for f in a:
893 try: os.unlink(repo.wjoin(f))
893 try: os.unlink(repo.wjoin(f))
894 except: raise
894 except: raise
895 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
895 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
896 except: pass
896 except: pass
897 if a:
897 if a:
898 repo.dirstate.forget(a)
898 repo.dirstate.forget(a)
899 repo.dirstate.setparents(qp, revlog.nullid)
899 repo.dirstate.setparents(qp, revlog.nullid)
900 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
900 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
901 del self.applied[start:end]
901 del self.applied[start:end]
902 if len(self.applied):
902 if len(self.applied):
903 self.ui.write("Now at: %s\n" % self.applied[-1].name)
903 self.ui.write("Now at: %s\n" % self.applied[-1].name)
904 else:
904 else:
905 self.ui.write("Patch queue now empty\n")
905 self.ui.write("Patch queue now empty\n")
906
906
907 def diff(self, repo, files):
907 def diff(self, repo, pats, opts):
908 top = self.check_toppatch(repo)
908 top = self.check_toppatch(repo)
909 if not top:
909 if not top:
910 self.ui.write("No patches applied\n")
910 self.ui.write("No patches applied\n")
911 return
911 return
912 qp = self.qparents(repo, top)
912 qp = self.qparents(repo, top)
913 self.printdiff(repo, qp, files=files)
913 self.printdiff(repo, qp, files=pats, opts=opts)
914
914
915 def refresh(self, repo, msg='', short=False):
915 def refresh(self, repo, pats=None, **opts):
916 if len(self.applied) == 0:
916 if len(self.applied) == 0:
917 self.ui.write("No patches applied\n")
917 self.ui.write("No patches applied\n")
918 return
918 return
919 wlock = repo.wlock()
919 wlock = repo.wlock()
920 self.check_toppatch(repo)
920 self.check_toppatch(repo)
921 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
921 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
922 top = revlog.bin(top)
922 top = revlog.bin(top)
923 cparents = repo.changelog.parents(top)
923 cparents = repo.changelog.parents(top)
924 patchparent = self.qparents(repo, top)
924 patchparent = self.qparents(repo, top)
925 message, comments, user, date, patchfound = self.readheaders(patch)
925 message, comments, user, date, patchfound = self.readheaders(patch)
926
926
927 patchf = self.opener(patch, "w")
927 patchf = self.opener(patch, "w")
928 msg = msg.rstrip()
928 msg = opts.get('msg', '').rstrip()
929 if msg:
929 if msg:
930 if comments:
930 if comments:
931 # Remove existing message.
931 # Remove existing message.
932 ci = 0
932 ci = 0
933 for mi in range(len(message)):
933 for mi in range(len(message)):
934 while message[mi] != comments[ci]:
934 while message[mi] != comments[ci]:
935 ci += 1
935 ci += 1
936 del comments[ci]
936 del comments[ci]
937 comments.append(msg)
937 comments.append(msg)
938 if comments:
938 if comments:
939 comments = "\n".join(comments) + '\n\n'
939 comments = "\n".join(comments) + '\n\n'
940 patchf.write(comments)
940 patchf.write(comments)
941
941
942 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
942 tip = repo.changelog.tip()
943 tip = repo.changelog.tip()
943 if top == tip:
944 if top == tip:
944 # if the top of our patch queue is also the tip, there is an
945 # if the top of our patch queue is also the tip, there is an
945 # optimization here. We update the dirstate in place and strip
946 # optimization here. We update the dirstate in place and strip
946 # off the tip commit. Then just commit the current directory
947 # off the tip commit. Then just commit the current directory
947 # tree. We can also send repo.commit the list of files
948 # tree. We can also send repo.commit the list of files
948 # changed to speed up the diff
949 # changed to speed up the diff
949 #
950 #
950 # in short mode, we only diff the files included in the
951 # in short mode, we only diff the files included in the
951 # patch already
952 # patch already
952 #
953 #
953 # this should really read:
954 # this should really read:
954 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
955 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
955 # but we do it backwards to take advantage of manifest/chlog
956 # but we do it backwards to take advantage of manifest/chlog
956 # caching against the next repo.status call
957 # caching against the next repo.status call
957 #
958 #
958 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
959 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
959 if short:
960 if opts.get('short'):
960 filelist = mm + aa + dd
961 filelist = mm + aa + dd
961 else:
962 else:
962 filelist = None
963 filelist = None
963 m, a, r, d, u = repo.status(files=filelist)[:5]
964 m, a, r, d, u = repo.status(files=filelist)[:5]
964
965
965 # we might end up with files that were added between tip and
966 # we might end up with files that were added between tip and
966 # the dirstate parent, but then changed in the local dirstate.
967 # the dirstate parent, but then changed in the local dirstate.
967 # in this case, we want them to only show up in the added section
968 # in this case, we want them to only show up in the added section
968 for x in m:
969 for x in m:
969 if x not in aa:
970 if x not in aa:
970 mm.append(x)
971 mm.append(x)
971 # we might end up with files added by the local dirstate that
972 # we might end up with files added by the local dirstate that
972 # were deleted by the patch. In this case, they should only
973 # were deleted by the patch. In this case, they should only
973 # show up in the changed section.
974 # show up in the changed section.
974 for x in a:
975 for x in a:
975 if x in dd:
976 if x in dd:
976 del dd[dd.index(x)]
977 del dd[dd.index(x)]
977 mm.append(x)
978 mm.append(x)
978 else:
979 else:
979 aa.append(x)
980 aa.append(x)
980 # make sure any files deleted in the local dirstate
981 # make sure any files deleted in the local dirstate
981 # are not in the add or change column of the patch
982 # are not in the add or change column of the patch
982 forget = []
983 forget = []
983 for x in d + r:
984 for x in d + r:
984 if x in aa:
985 if x in aa:
985 del aa[aa.index(x)]
986 del aa[aa.index(x)]
986 forget.append(x)
987 forget.append(x)
987 continue
988 continue
988 elif x in mm:
989 elif x in mm:
989 del mm[mm.index(x)]
990 del mm[mm.index(x)]
990 dd.append(x)
991 dd.append(x)
991
992
992 m = list(util.unique(mm))
993 m = list(util.unique(mm))
993 r = list(util.unique(dd))
994 r = list(util.unique(dd))
994 a = list(util.unique(aa))
995 a = list(util.unique(aa))
995 filelist = list(util.unique(m + r + a))
996 filelist = filter(matchfn, util.unique(m + r + a))
996 self.printdiff(repo, patchparent, files=filelist,
997 self.printdiff(repo, patchparent, files=filelist,
997 changes=(m, a, r, [], u), fp=patchf)
998 changes=(m, a, r, [], u), fp=patchf)
998 patchf.close()
999 patchf.close()
999
1000
1000 changes = repo.changelog.read(tip)
1001 changes = repo.changelog.read(tip)
1001 repo.dirstate.setparents(*cparents)
1002 repo.dirstate.setparents(*cparents)
1003 copies = [(f, repo.dirstate.copied(f)) for f in a]
1002 repo.dirstate.update(a, 'a')
1004 repo.dirstate.update(a, 'a')
1005 for dst, src in copies:
1006 repo.dirstate.copy(src, dst)
1003 repo.dirstate.update(r, 'r')
1007 repo.dirstate.update(r, 'r')
1008 # if the patch excludes a modified file, mark that file with mtime=0
1009 # so status can see it.
1010 mm = []
1011 for i in range(len(m)-1, -1, -1):
1012 if not matchfn(m[i]):
1013 mm.append(m[i])
1014 del m[i]
1004 repo.dirstate.update(m, 'n')
1015 repo.dirstate.update(m, 'n')
1016 repo.dirstate.update(mm, 'n', st_mtime=0)
1005 repo.dirstate.forget(forget)
1017 repo.dirstate.forget(forget)
1006
1018
1007 if not msg:
1019 if not msg:
1008 if not message:
1020 if not message:
1009 message = "patch queue: %s\n" % patch
1021 message = "patch queue: %s\n" % patch
1010 else:
1022 else:
1011 message = "\n".join(message)
1023 message = "\n".join(message)
1012 else:
1024 else:
1013 message = msg
1025 message = msg
1014
1026
1015 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1027 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1016 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1028 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1017 self.applied[-1] = statusentry(revlog.hex(n), patch)
1029 self.applied[-1] = statusentry(revlog.hex(n), patch)
1018 self.applied_dirty = 1
1030 self.applied_dirty = 1
1019 else:
1031 else:
1020 self.printdiff(repo, patchparent, fp=patchf)
1032 self.printdiff(repo, patchparent, fp=patchf)
1021 patchf.close()
1033 patchf.close()
1022 self.pop(repo, force=True, wlock=wlock)
1034 self.pop(repo, force=True, wlock=wlock)
1023 self.push(repo, force=True, wlock=wlock)
1035 self.push(repo, force=True, wlock=wlock)
1024
1036
1025 def init(self, repo, create=False):
1037 def init(self, repo, create=False):
1026 if os.path.isdir(self.path):
1038 if os.path.isdir(self.path):
1027 raise util.Abort(_("patch queue directory already exists"))
1039 raise util.Abort(_("patch queue directory already exists"))
1028 os.mkdir(self.path)
1040 os.mkdir(self.path)
1029 if create:
1041 if create:
1030 return self.qrepo(create=True)
1042 return self.qrepo(create=True)
1031
1043
1032 def unapplied(self, repo, patch=None):
1044 def unapplied(self, repo, patch=None):
1033 if patch and patch not in self.series:
1045 if patch and patch not in self.series:
1034 raise util.Abort(_("patch %s is not in series file") % patch)
1046 raise util.Abort(_("patch %s is not in series file") % patch)
1035 if not patch:
1047 if not patch:
1036 start = self.series_end()
1048 start = self.series_end()
1037 else:
1049 else:
1038 start = self.series.index(patch) + 1
1050 start = self.series.index(patch) + 1
1039 unapplied = []
1051 unapplied = []
1040 for i in xrange(start, len(self.series)):
1052 for i in xrange(start, len(self.series)):
1041 pushable, reason = self.pushable(i)
1053 pushable, reason = self.pushable(i)
1042 if pushable:
1054 if pushable:
1043 unapplied.append((i, self.series[i]))
1055 unapplied.append((i, self.series[i]))
1044 self.explain_pushable(i)
1056 self.explain_pushable(i)
1045 return unapplied
1057 return unapplied
1046
1058
1047 def qseries(self, repo, missing=None, summary=False):
1059 def qseries(self, repo, missing=None, summary=False):
1048 start = self.series_end(all_patches=True)
1060 start = self.series_end(all_patches=True)
1049 if not missing:
1061 if not missing:
1050 for i in range(len(self.series)):
1062 for i in range(len(self.series)):
1051 patch = self.series[i]
1063 patch = self.series[i]
1052 if self.ui.verbose:
1064 if self.ui.verbose:
1053 if i < start:
1065 if i < start:
1054 status = 'A'
1066 status = 'A'
1055 elif self.pushable(i)[0]:
1067 elif self.pushable(i)[0]:
1056 status = 'U'
1068 status = 'U'
1057 else:
1069 else:
1058 status = 'G'
1070 status = 'G'
1059 self.ui.write('%d %s ' % (i, status))
1071 self.ui.write('%d %s ' % (i, status))
1060 if summary:
1072 if summary:
1061 msg = self.readheaders(patch)[0]
1073 msg = self.readheaders(patch)[0]
1062 msg = msg and ': ' + msg[0] or ': '
1074 msg = msg and ': ' + msg[0] or ': '
1063 else:
1075 else:
1064 msg = ''
1076 msg = ''
1065 self.ui.write('%s%s\n' % (patch, msg))
1077 self.ui.write('%s%s\n' % (patch, msg))
1066 else:
1078 else:
1067 msng_list = []
1079 msng_list = []
1068 for root, dirs, files in os.walk(self.path):
1080 for root, dirs, files in os.walk(self.path):
1069 d = root[len(self.path) + 1:]
1081 d = root[len(self.path) + 1:]
1070 for f in files:
1082 for f in files:
1071 fl = os.path.join(d, f)
1083 fl = os.path.join(d, f)
1072 if (fl not in self.series and
1084 if (fl not in self.series and
1073 fl not in (self.status_path, self.series_path)
1085 fl not in (self.status_path, self.series_path)
1074 and not fl.startswith('.')):
1086 and not fl.startswith('.')):
1075 msng_list.append(fl)
1087 msng_list.append(fl)
1076 msng_list.sort()
1088 msng_list.sort()
1077 for x in msng_list:
1089 for x in msng_list:
1078 if self.ui.verbose:
1090 if self.ui.verbose:
1079 self.ui.write("D ")
1091 self.ui.write("D ")
1080 self.ui.write("%s\n" % x)
1092 self.ui.write("%s\n" % x)
1081
1093
1082 def issaveline(self, l):
1094 def issaveline(self, l):
1083 if l.name == '.hg.patches.save.line':
1095 if l.name == '.hg.patches.save.line':
1084 return True
1096 return True
1085
1097
1086 def qrepo(self, create=False):
1098 def qrepo(self, create=False):
1087 if create or os.path.isdir(self.join(".hg")):
1099 if create or os.path.isdir(self.join(".hg")):
1088 return hg.repository(self.ui, path=self.path, create=create)
1100 return hg.repository(self.ui, path=self.path, create=create)
1089
1101
1090 def restore(self, repo, rev, delete=None, qupdate=None):
1102 def restore(self, repo, rev, delete=None, qupdate=None):
1091 c = repo.changelog.read(rev)
1103 c = repo.changelog.read(rev)
1092 desc = c[4].strip()
1104 desc = c[4].strip()
1093 lines = desc.splitlines()
1105 lines = desc.splitlines()
1094 i = 0
1106 i = 0
1095 datastart = None
1107 datastart = None
1096 series = []
1108 series = []
1097 applied = []
1109 applied = []
1098 qpp = None
1110 qpp = None
1099 for i in xrange(0, len(lines)):
1111 for i in xrange(0, len(lines)):
1100 if lines[i] == 'Patch Data:':
1112 if lines[i] == 'Patch Data:':
1101 datastart = i + 1
1113 datastart = i + 1
1102 elif lines[i].startswith('Dirstate:'):
1114 elif lines[i].startswith('Dirstate:'):
1103 l = lines[i].rstrip()
1115 l = lines[i].rstrip()
1104 l = l[10:].split(' ')
1116 l = l[10:].split(' ')
1105 qpp = [ hg.bin(x) for x in l ]
1117 qpp = [ hg.bin(x) for x in l ]
1106 elif datastart != None:
1118 elif datastart != None:
1107 l = lines[i].rstrip()
1119 l = lines[i].rstrip()
1108 se = statusentry(l)
1120 se = statusentry(l)
1109 file_ = se.name
1121 file_ = se.name
1110 if se.rev:
1122 if se.rev:
1111 applied.append(se)
1123 applied.append(se)
1112 series.append(file_)
1124 series.append(file_)
1113 if datastart == None:
1125 if datastart == None:
1114 self.ui.warn("No saved patch data found\n")
1126 self.ui.warn("No saved patch data found\n")
1115 return 1
1127 return 1
1116 self.ui.warn("restoring status: %s\n" % lines[0])
1128 self.ui.warn("restoring status: %s\n" % lines[0])
1117 self.full_series = series
1129 self.full_series = series
1118 self.applied = applied
1130 self.applied = applied
1119 self.parse_series()
1131 self.parse_series()
1120 self.series_dirty = 1
1132 self.series_dirty = 1
1121 self.applied_dirty = 1
1133 self.applied_dirty = 1
1122 heads = repo.changelog.heads()
1134 heads = repo.changelog.heads()
1123 if delete:
1135 if delete:
1124 if rev not in heads:
1136 if rev not in heads:
1125 self.ui.warn("save entry has children, leaving it alone\n")
1137 self.ui.warn("save entry has children, leaving it alone\n")
1126 else:
1138 else:
1127 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1139 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1128 pp = repo.dirstate.parents()
1140 pp = repo.dirstate.parents()
1129 if rev in pp:
1141 if rev in pp:
1130 update = True
1142 update = True
1131 else:
1143 else:
1132 update = False
1144 update = False
1133 self.strip(repo, rev, update=update, backup='strip')
1145 self.strip(repo, rev, update=update, backup='strip')
1134 if qpp:
1146 if qpp:
1135 self.ui.warn("saved queue repository parents: %s %s\n" %
1147 self.ui.warn("saved queue repository parents: %s %s\n" %
1136 (hg.short(qpp[0]), hg.short(qpp[1])))
1148 (hg.short(qpp[0]), hg.short(qpp[1])))
1137 if qupdate:
1149 if qupdate:
1138 print "queue directory updating"
1150 print "queue directory updating"
1139 r = self.qrepo()
1151 r = self.qrepo()
1140 if not r:
1152 if not r:
1141 self.ui.warn("Unable to load queue repository\n")
1153 self.ui.warn("Unable to load queue repository\n")
1142 return 1
1154 return 1
1143 hg.clean(r, qpp[0])
1155 hg.clean(r, qpp[0])
1144
1156
1145 def save(self, repo, msg=None):
1157 def save(self, repo, msg=None):
1146 if len(self.applied) == 0:
1158 if len(self.applied) == 0:
1147 self.ui.warn("save: no patches applied, exiting\n")
1159 self.ui.warn("save: no patches applied, exiting\n")
1148 return 1
1160 return 1
1149 if self.issaveline(self.applied[-1]):
1161 if self.issaveline(self.applied[-1]):
1150 self.ui.warn("status is already saved\n")
1162 self.ui.warn("status is already saved\n")
1151 return 1
1163 return 1
1152
1164
1153 ar = [ ':' + x for x in self.full_series ]
1165 ar = [ ':' + x for x in self.full_series ]
1154 if not msg:
1166 if not msg:
1155 msg = "hg patches saved state"
1167 msg = "hg patches saved state"
1156 else:
1168 else:
1157 msg = "hg patches: " + msg.rstrip('\r\n')
1169 msg = "hg patches: " + msg.rstrip('\r\n')
1158 r = self.qrepo()
1170 r = self.qrepo()
1159 if r:
1171 if r:
1160 pp = r.dirstate.parents()
1172 pp = r.dirstate.parents()
1161 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1173 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1162 msg += "\n\nPatch Data:\n"
1174 msg += "\n\nPatch Data:\n"
1163 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1175 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1164 "\n".join(ar) + '\n' or "")
1176 "\n".join(ar) + '\n' or "")
1165 n = repo.commit(None, text, user=None, force=1)
1177 n = repo.commit(None, text, user=None, force=1)
1166 if not n:
1178 if not n:
1167 self.ui.warn("repo commit failed\n")
1179 self.ui.warn("repo commit failed\n")
1168 return 1
1180 return 1
1169 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1181 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1170 self.applied_dirty = 1
1182 self.applied_dirty = 1
1171
1183
1172 def full_series_end(self):
1184 def full_series_end(self):
1173 if len(self.applied) > 0:
1185 if len(self.applied) > 0:
1174 p = self.applied[-1].name
1186 p = self.applied[-1].name
1175 end = self.find_series(p)
1187 end = self.find_series(p)
1176 if end == None:
1188 if end == None:
1177 return len(self.full_series)
1189 return len(self.full_series)
1178 return end + 1
1190 return end + 1
1179 return 0
1191 return 0
1180
1192
1181 def series_end(self, all_patches=False):
1193 def series_end(self, all_patches=False):
1182 end = 0
1194 end = 0
1183 def next(start):
1195 def next(start):
1184 if all_patches:
1196 if all_patches:
1185 return start
1197 return start
1186 i = start
1198 i = start
1187 while i < len(self.series):
1199 while i < len(self.series):
1188 p, reason = self.pushable(i)
1200 p, reason = self.pushable(i)
1189 if p:
1201 if p:
1190 break
1202 break
1191 self.explain_pushable(i)
1203 self.explain_pushable(i)
1192 i += 1
1204 i += 1
1193 return i
1205 return i
1194 if len(self.applied) > 0:
1206 if len(self.applied) > 0:
1195 p = self.applied[-1].name
1207 p = self.applied[-1].name
1196 try:
1208 try:
1197 end = self.series.index(p)
1209 end = self.series.index(p)
1198 except ValueError:
1210 except ValueError:
1199 return 0
1211 return 0
1200 return next(end + 1)
1212 return next(end + 1)
1201 return next(end)
1213 return next(end)
1202
1214
1203 def qapplied(self, repo, patch=None):
1215 def qapplied(self, repo, patch=None):
1204 if patch and patch not in self.series:
1216 if patch and patch not in self.series:
1205 raise util.Abort(_("patch %s is not in series file") % patch)
1217 raise util.Abort(_("patch %s is not in series file") % patch)
1206 if not patch:
1218 if not patch:
1207 end = len(self.applied)
1219 end = len(self.applied)
1208 else:
1220 else:
1209 end = self.series.index(patch) + 1
1221 end = self.series.index(patch) + 1
1210 for x in xrange(end):
1222 for x in xrange(end):
1211 p = self.appliedname(x)
1223 p = self.appliedname(x)
1212 self.ui.write("%s\n" % p)
1224 self.ui.write("%s\n" % p)
1213
1225
1214 def appliedname(self, index):
1226 def appliedname(self, index):
1215 pname = self.applied[index].name
1227 pname = self.applied[index].name
1216 if not self.ui.verbose:
1228 if not self.ui.verbose:
1217 p = pname
1229 p = pname
1218 else:
1230 else:
1219 p = str(self.series.index(pname)) + " " + p
1231 p = str(self.series.index(pname)) + " " + pname
1220 return p
1232 return p
1221
1233
1222 def top(self, repo):
1234 def top(self, repo):
1223 if len(self.applied):
1235 if len(self.applied):
1224 p = self.appliedname(-1)
1236 p = self.appliedname(-1)
1225 self.ui.write(p + '\n')
1237 self.ui.write(p + '\n')
1226 else:
1238 else:
1227 self.ui.write("No patches applied\n")
1239 self.ui.write("No patches applied\n")
1228
1240
1229 def next(self, repo):
1241 def next(self, repo):
1230 end = self.series_end()
1242 end = self.series_end()
1231 if end == len(self.series):
1243 if end == len(self.series):
1232 self.ui.write("All patches applied\n")
1244 self.ui.write("All patches applied\n")
1233 else:
1245 else:
1234 p = self.series[end]
1246 p = self.series[end]
1235 if self.ui.verbose:
1247 if self.ui.verbose:
1236 self.ui.write("%d " % self.series.index(p))
1248 self.ui.write("%d " % self.series.index(p))
1237 self.ui.write(p + '\n')
1249 self.ui.write(p + '\n')
1238
1250
1239 def prev(self, repo):
1251 def prev(self, repo):
1240 if len(self.applied) > 1:
1252 if len(self.applied) > 1:
1241 p = self.appliedname(-2)
1253 p = self.appliedname(-2)
1242 self.ui.write(p + '\n')
1254 self.ui.write(p + '\n')
1243 elif len(self.applied) == 1:
1255 elif len(self.applied) == 1:
1244 self.ui.write("Only one patch applied\n")
1256 self.ui.write("Only one patch applied\n")
1245 else:
1257 else:
1246 self.ui.write("No patches applied\n")
1258 self.ui.write("No patches applied\n")
1247
1259
1248 def qimport(self, repo, files, patch=None, existing=None, force=None):
1260 def qimport(self, repo, files, patch=None, existing=None, force=None):
1249 if len(files) > 1 and patch:
1261 if len(files) > 1 and patch:
1250 raise util.Abort(_('option "-n" not valid when importing multiple '
1262 raise util.Abort(_('option "-n" not valid when importing multiple '
1251 'files'))
1263 'files'))
1252 i = 0
1264 i = 0
1253 added = []
1265 added = []
1254 for filename in files:
1266 for filename in files:
1255 if existing:
1267 if existing:
1256 if not patch:
1268 if not patch:
1257 patch = filename
1269 patch = filename
1258 if not os.path.isfile(self.join(patch)):
1270 if not os.path.isfile(self.join(patch)):
1259 raise util.Abort(_("patch %s does not exist") % patch)
1271 raise util.Abort(_("patch %s does not exist") % patch)
1260 else:
1272 else:
1261 try:
1273 try:
1262 text = file(filename).read()
1274 text = file(filename).read()
1263 except IOError:
1275 except IOError:
1264 raise util.Abort(_("unable to read %s") % patch)
1276 raise util.Abort(_("unable to read %s") % patch)
1265 if not patch:
1277 if not patch:
1266 patch = os.path.split(filename)[1]
1278 patch = os.path.split(filename)[1]
1267 if not force and os.path.exists(self.join(patch)):
1279 if not force and os.path.exists(self.join(patch)):
1268 raise util.Abort(_('patch "%s" already exists') % patch)
1280 raise util.Abort(_('patch "%s" already exists') % patch)
1269 patchf = self.opener(patch, "w")
1281 patchf = self.opener(patch, "w")
1270 patchf.write(text)
1282 patchf.write(text)
1271 if patch in self.series:
1283 if patch in self.series:
1272 raise util.Abort(_('patch %s is already in the series file')
1284 raise util.Abort(_('patch %s is already in the series file')
1273 % patch)
1285 % patch)
1274 index = self.full_series_end() + i
1286 index = self.full_series_end() + i
1275 self.full_series[index:index] = [patch]
1287 self.full_series[index:index] = [patch]
1276 self.parse_series()
1288 self.parse_series()
1277 self.ui.warn("adding %s to series file\n" % patch)
1289 self.ui.warn("adding %s to series file\n" % patch)
1278 i += 1
1290 i += 1
1279 added.append(patch)
1291 added.append(patch)
1280 patch = None
1292 patch = None
1281 self.series_dirty = 1
1293 self.series_dirty = 1
1282 qrepo = self.qrepo()
1294 qrepo = self.qrepo()
1283 if qrepo:
1295 if qrepo:
1284 qrepo.add(added)
1296 qrepo.add(added)
1285
1297
1286 def delete(ui, repo, patch, *patches, **opts):
1298 def delete(ui, repo, patch, *patches, **opts):
1287 """remove patches from queue
1299 """remove patches from queue
1288
1300
1289 The patches must not be applied.
1301 The patches must not be applied.
1290 With -k, the patch files are preserved in the patch directory."""
1302 With -k, the patch files are preserved in the patch directory."""
1291 q = repo.mq
1303 q = repo.mq
1292 q.delete(repo, (patch,) + patches, keep=opts.get('keep'))
1304 q.delete(repo, (patch,) + patches, keep=opts.get('keep'))
1293 q.save_dirty()
1305 q.save_dirty()
1294 return 0
1306 return 0
1295
1307
1296 def applied(ui, repo, patch=None, **opts):
1308 def applied(ui, repo, patch=None, **opts):
1297 """print the patches already applied"""
1309 """print the patches already applied"""
1298 repo.mq.qapplied(repo, patch)
1310 repo.mq.qapplied(repo, patch)
1299 return 0
1311 return 0
1300
1312
1301 def unapplied(ui, repo, patch=None, **opts):
1313 def unapplied(ui, repo, patch=None, **opts):
1302 """print the patches not yet applied"""
1314 """print the patches not yet applied"""
1303 for i, p in repo.mq.unapplied(repo, patch):
1315 for i, p in repo.mq.unapplied(repo, patch):
1304 if ui.verbose:
1316 if ui.verbose:
1305 ui.write("%d " % i)
1317 ui.write("%d " % i)
1306 ui.write("%s\n" % p)
1318 ui.write("%s\n" % p)
1307
1319
1308 def qimport(ui, repo, *filename, **opts):
1320 def qimport(ui, repo, *filename, **opts):
1309 """import a patch"""
1321 """import a patch"""
1310 q = repo.mq
1322 q = repo.mq
1311 q.qimport(repo, filename, patch=opts['name'],
1323 q.qimport(repo, filename, patch=opts['name'],
1312 existing=opts['existing'], force=opts['force'])
1324 existing=opts['existing'], force=opts['force'])
1313 q.save_dirty()
1325 q.save_dirty()
1314 return 0
1326 return 0
1315
1327
1316 def init(ui, repo, **opts):
1328 def init(ui, repo, **opts):
1317 """init a new queue repository
1329 """init a new queue repository
1318
1330
1319 The queue repository is unversioned by default. If -c is
1331 The queue repository is unversioned by default. If -c is
1320 specified, qinit will create a separate nested repository
1332 specified, qinit will create a separate nested repository
1321 for patches. Use qcommit to commit changes to this queue
1333 for patches. Use qcommit to commit changes to this queue
1322 repository."""
1334 repository."""
1323 q = repo.mq
1335 q = repo.mq
1324 r = q.init(repo, create=opts['create_repo'])
1336 r = q.init(repo, create=opts['create_repo'])
1325 q.save_dirty()
1337 q.save_dirty()
1326 if r:
1338 if r:
1327 fp = r.wopener('.hgignore', 'w')
1339 fp = r.wopener('.hgignore', 'w')
1328 print >> fp, 'syntax: glob'
1340 print >> fp, 'syntax: glob'
1329 print >> fp, 'status'
1341 print >> fp, 'status'
1330 fp.close()
1342 fp.close()
1331 r.wopener('series', 'w').close()
1343 r.wopener('series', 'w').close()
1332 r.add(['.hgignore', 'series'])
1344 r.add(['.hgignore', 'series'])
1333 return 0
1345 return 0
1334
1346
1335 def clone(ui, source, dest=None, **opts):
1347 def clone(ui, source, dest=None, **opts):
1336 '''clone main and patch repository at same time
1348 '''clone main and patch repository at same time
1337
1349
1338 If source is local, destination will have no patches applied. If
1350 If source is local, destination will have no patches applied. If
1339 source is remote, this command can not check if patches are
1351 source is remote, this command can not check if patches are
1340 applied in source, so cannot guarantee that patches are not
1352 applied in source, so cannot guarantee that patches are not
1341 applied in destination. If you clone remote repository, be sure
1353 applied in destination. If you clone remote repository, be sure
1342 before that it has no patches applied.
1354 before that it has no patches applied.
1343
1355
1344 Source patch repository is looked for in <src>/.hg/patches by
1356 Source patch repository is looked for in <src>/.hg/patches by
1345 default. Use -p <url> to change.
1357 default. Use -p <url> to change.
1346 '''
1358 '''
1347 commands.setremoteconfig(ui, opts)
1359 commands.setremoteconfig(ui, opts)
1348 if dest is None:
1360 if dest is None:
1349 dest = hg.defaultdest(source)
1361 dest = hg.defaultdest(source)
1350 sr = hg.repository(ui, ui.expandpath(source))
1362 sr = hg.repository(ui, ui.expandpath(source))
1351 qbase, destrev = None, None
1363 qbase, destrev = None, None
1352 if sr.local():
1364 if sr.local():
1353 reposetup(ui, sr)
1365 reposetup(ui, sr)
1354 if sr.mq.applied:
1366 if sr.mq.applied:
1355 qbase = revlog.bin(sr.mq.applied[0].rev)
1367 qbase = revlog.bin(sr.mq.applied[0].rev)
1356 if not hg.islocal(dest):
1368 if not hg.islocal(dest):
1357 destrev = sr.parents(qbase)[0]
1369 destrev = sr.parents(qbase)[0]
1358 ui.note(_('cloning main repo\n'))
1370 ui.note(_('cloning main repo\n'))
1359 sr, dr = hg.clone(ui, sr, dest,
1371 sr, dr = hg.clone(ui, sr, dest,
1360 pull=opts['pull'],
1372 pull=opts['pull'],
1361 rev=destrev,
1373 rev=destrev,
1362 update=False,
1374 update=False,
1363 stream=opts['uncompressed'])
1375 stream=opts['uncompressed'])
1364 ui.note(_('cloning patch repo\n'))
1376 ui.note(_('cloning patch repo\n'))
1365 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1377 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1366 dr.url() + '/.hg/patches',
1378 dr.url() + '/.hg/patches',
1367 pull=opts['pull'],
1379 pull=opts['pull'],
1368 update=not opts['noupdate'],
1380 update=not opts['noupdate'],
1369 stream=opts['uncompressed'])
1381 stream=opts['uncompressed'])
1370 if dr.local():
1382 if dr.local():
1371 if qbase:
1383 if qbase:
1372 ui.note(_('stripping applied patches from destination repo\n'))
1384 ui.note(_('stripping applied patches from destination repo\n'))
1373 reposetup(ui, dr)
1385 reposetup(ui, dr)
1374 dr.mq.strip(dr, qbase, update=False, backup=None)
1386 dr.mq.strip(dr, qbase, update=False, backup=None)
1375 if not opts['noupdate']:
1387 if not opts['noupdate']:
1376 ui.note(_('updating destination repo\n'))
1388 ui.note(_('updating destination repo\n'))
1377 hg.update(dr, dr.changelog.tip())
1389 hg.update(dr, dr.changelog.tip())
1378
1390
1379 def commit(ui, repo, *pats, **opts):
1391 def commit(ui, repo, *pats, **opts):
1380 """commit changes in the queue repository"""
1392 """commit changes in the queue repository"""
1381 q = repo.mq
1393 q = repo.mq
1382 r = q.qrepo()
1394 r = q.qrepo()
1383 if not r: raise util.Abort('no queue repository')
1395 if not r: raise util.Abort('no queue repository')
1384 commands.commit(r.ui, r, *pats, **opts)
1396 commands.commit(r.ui, r, *pats, **opts)
1385
1397
1386 def series(ui, repo, **opts):
1398 def series(ui, repo, **opts):
1387 """print the entire series file"""
1399 """print the entire series file"""
1388 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1400 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1389 return 0
1401 return 0
1390
1402
1391 def top(ui, repo, **opts):
1403 def top(ui, repo, **opts):
1392 """print the name of the current patch"""
1404 """print the name of the current patch"""
1393 repo.mq.top(repo)
1405 repo.mq.top(repo)
1394 return 0
1406 return 0
1395
1407
1396 def next(ui, repo, **opts):
1408 def next(ui, repo, **opts):
1397 """print the name of the next patch"""
1409 """print the name of the next patch"""
1398 repo.mq.next(repo)
1410 repo.mq.next(repo)
1399 return 0
1411 return 0
1400
1412
1401 def prev(ui, repo, **opts):
1413 def prev(ui, repo, **opts):
1402 """print the name of the previous patch"""
1414 """print the name of the previous patch"""
1403 repo.mq.prev(repo)
1415 repo.mq.prev(repo)
1404 return 0
1416 return 0
1405
1417
1406 def new(ui, repo, patch, **opts):
1418 def new(ui, repo, patch, **opts):
1407 """create a new patch
1419 """create a new patch
1408
1420
1409 qnew creates a new patch on top of the currently-applied patch
1421 qnew creates a new patch on top of the currently-applied patch
1410 (if any). It will refuse to run if there are any outstanding
1422 (if any). It will refuse to run if there are any outstanding
1411 changes unless -f is specified, in which case the patch will
1423 changes unless -f is specified, in which case the patch will
1412 be initialised with them.
1424 be initialised with them.
1413
1425
1414 -m or -l set the patch header as well as the commit message.
1426 -e, -m or -l set the patch header as well as the commit message.
1415 If neither is specified, the patch header is empty and the
1427 If none is specified, the patch header is empty and the
1416 commit message is 'New patch: PATCH'"""
1428 commit message is 'New patch: PATCH'"""
1417 q = repo.mq
1429 q = repo.mq
1418 message = commands.logmessage(opts)
1430 message = commands.logmessage(opts)
1431 if opts['edit']:
1432 message = ui.edit(message, ui.username())
1419 q.new(repo, patch, msg=message, force=opts['force'])
1433 q.new(repo, patch, msg=message, force=opts['force'])
1420 q.save_dirty()
1434 q.save_dirty()
1421 return 0
1435 return 0
1422
1436
1423 def refresh(ui, repo, **opts):
1437 def refresh(ui, repo, *pats, **opts):
1424 """update the current patch"""
1438 """update the current patch
1439
1440 If any file patterns are provided, the refreshed patch will contain only
1441 the modifications that match those patterns; the remaining modifications
1442 will remain in the working directory.
1443 """
1425 q = repo.mq
1444 q = repo.mq
1426 message = commands.logmessage(opts)
1445 message = commands.logmessage(opts)
1427 if opts['edit']:
1446 if opts['edit']:
1428 if message:
1447 if message:
1429 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1448 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1430 patch = q.applied[-1].name
1449 patch = q.applied[-1].name
1431 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1450 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1432 message = ui.edit('\n'.join(message), user or ui.username())
1451 message = ui.edit('\n'.join(message), user or ui.username())
1433 q.refresh(repo, msg=message, short=opts['short'])
1452 q.refresh(repo, pats, msg=message, **opts)
1434 q.save_dirty()
1453 q.save_dirty()
1435 return 0
1454 return 0
1436
1455
1437 def diff(ui, repo, *files, **opts):
1456 def diff(ui, repo, *pats, **opts):
1438 """diff of the current patch"""
1457 """diff of the current patch"""
1439 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1458 repo.mq.diff(repo, pats, opts)
1440 repo.mq.diff(repo, list(files))
1441 return 0
1459 return 0
1442
1460
1443 def fold(ui, repo, *files, **opts):
1461 def fold(ui, repo, *files, **opts):
1444 """fold the named patches into the current patch
1462 """fold the named patches into the current patch
1445
1463
1446 Patches must not yet be applied. Each patch will be successively
1464 Patches must not yet be applied. Each patch will be successively
1447 applied to the current patch in the order given. If all the
1465 applied to the current patch in the order given. If all the
1448 patches apply successfully, the current patch will be refreshed
1466 patches apply successfully, the current patch will be refreshed
1449 with the new cumulative patch, and the folded patches will
1467 with the new cumulative patch, and the folded patches will
1450 be deleted. With -k/--keep, the folded patch files will not
1468 be deleted. With -k/--keep, the folded patch files will not
1451 be removed afterwards.
1469 be removed afterwards.
1452
1470
1453 The header for each folded patch will be concatenated with
1471 The header for each folded patch will be concatenated with
1454 the current patch header, separated by a line of '* * *'."""
1472 the current patch header, separated by a line of '* * *'."""
1455
1473
1456 q = repo.mq
1474 q = repo.mq
1457
1475
1458 if not files:
1476 if not files:
1459 raise util.Abort(_('qfold requires at least one patch name'))
1477 raise util.Abort(_('qfold requires at least one patch name'))
1460 if not q.check_toppatch(repo):
1478 if not q.check_toppatch(repo):
1461 raise util.Abort(_('No patches applied\n'))
1479 raise util.Abort(_('No patches applied\n'))
1462
1480
1463 message = commands.logmessage(opts)
1481 message = commands.logmessage(opts)
1464 if opts['edit']:
1482 if opts['edit']:
1465 if message:
1483 if message:
1466 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1484 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1467
1485
1468 parent = q.lookup('qtip')
1486 parent = q.lookup('qtip')
1469 patches = []
1487 patches = []
1470 messages = []
1488 messages = []
1471 for f in files:
1489 for f in files:
1472 patch = q.lookup(f)
1490 p = q.lookup(f)
1473 if patch in patches or patch == parent:
1491 if p in patches or p == parent:
1474 ui.warn(_('Skipping already folded patch %s') % patch)
1492 ui.warn(_('Skipping already folded patch %s') % p)
1475 if q.isapplied(patch):
1493 if q.isapplied(p):
1476 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1494 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1477 patches.append(patch)
1495 patches.append(p)
1478
1496
1479 for patch in patches:
1497 for p in patches:
1480 if not message:
1498 if not message:
1481 messages.append(q.readheaders(patch)[0])
1499 messages.append(q.readheaders(p)[0])
1482 pf = q.join(patch)
1500 pf = q.join(p)
1483 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1501 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1484 if not patchsuccess:
1502 if not patchsuccess:
1485 raise util.Abort(_('Error folding patch %s') % patch)
1503 raise util.Abort(_('Error folding patch %s') % p)
1504 patch.updatedir(ui, repo, files)
1486
1505
1487 if not message:
1506 if not message:
1488 message, comments, user = q.readheaders(parent)[0:3]
1507 message, comments, user = q.readheaders(parent)[0:3]
1489 for msg in messages:
1508 for msg in messages:
1490 message.append('* * *')
1509 message.append('* * *')
1491 message.extend(msg)
1510 message.extend(msg)
1492 message = '\n'.join(message)
1511 message = '\n'.join(message)
1493
1512
1494 if opts['edit']:
1513 if opts['edit']:
1495 message = ui.edit(message, user or ui.username())
1514 message = ui.edit(message, user or ui.username())
1496
1515
1497 q.refresh(repo, msg=message)
1516 q.refresh(repo, msg=message)
1498
1517 q.delete(repo, patches, keep=opts['keep'])
1499 for patch in patches:
1500 q.delete(repo, patch, keep=opts['keep'])
1501
1502 q.save_dirty()
1518 q.save_dirty()
1503
1519
1504 def guard(ui, repo, *args, **opts):
1520 def guard(ui, repo, *args, **opts):
1505 '''set or print guards for a patch
1521 '''set or print guards for a patch
1506
1522
1507 guards control whether a patch can be pushed. a patch with no
1523 Guards control whether a patch can be pushed. A patch with no
1508 guards is aways pushed. a patch with posative guard ("+foo") is
1524 guards is always pushed. A patch with a positive guard ("+foo") is
1509 pushed only if qselect command enables guard "foo". a patch with
1525 pushed only if the qselect command has activated it. A patch with
1510 nagative guard ("-foo") is never pushed if qselect command enables
1526 a negative guard ("-foo") is never pushed if the qselect command
1511 guard "foo".
1527 has activated it.
1512
1528
1513 with no arguments, default is to print current active guards.
1529 With no arguments, print the currently active guards.
1514 with arguments, set active guards for patch.
1530 With arguments, set guards for the named patch.
1515
1531
1516 to set nagative guard "-foo" on topmost patch ("--" is needed so
1532 To set a negative guard "-foo" on topmost patch ("--" is needed so
1517 hg will not interpret "-foo" as argument):
1533 hg will not interpret "-foo" as an option):
1518 hg qguard -- -foo
1534 hg qguard -- -foo
1519
1535
1520 to set guards on other patch:
1536 To set guards on another patch:
1521 hg qguard other.patch +2.6.17 -stable
1537 hg qguard other.patch +2.6.17 -stable
1522 '''
1538 '''
1523 def status(idx):
1539 def status(idx):
1524 guards = q.series_guards[idx] or ['unguarded']
1540 guards = q.series_guards[idx] or ['unguarded']
1525 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1541 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1526 q = repo.mq
1542 q = repo.mq
1527 patch = None
1543 patch = None
1528 args = list(args)
1544 args = list(args)
1529 if opts['list']:
1545 if opts['list']:
1530 if args or opts['none']:
1546 if args or opts['none']:
1531 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1547 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1532 for i in xrange(len(q.series)):
1548 for i in xrange(len(q.series)):
1533 status(i)
1549 status(i)
1534 return
1550 return
1535 if not args or args[0][0:1] in '-+':
1551 if not args or args[0][0:1] in '-+':
1536 if not q.applied:
1552 if not q.applied:
1537 raise util.Abort(_('no patches applied'))
1553 raise util.Abort(_('no patches applied'))
1538 patch = q.applied[-1].name
1554 patch = q.applied[-1].name
1539 if patch is None and args[0][0:1] not in '-+':
1555 if patch is None and args[0][0:1] not in '-+':
1540 patch = args.pop(0)
1556 patch = args.pop(0)
1541 if patch is None:
1557 if patch is None:
1542 raise util.Abort(_('no patch to work with'))
1558 raise util.Abort(_('no patch to work with'))
1543 if args or opts['none']:
1559 if args or opts['none']:
1544 q.set_guards(q.find_series(patch), args)
1560 q.set_guards(q.find_series(patch), args)
1545 q.save_dirty()
1561 q.save_dirty()
1546 else:
1562 else:
1547 status(q.series.index(q.lookup(patch)))
1563 status(q.series.index(q.lookup(patch)))
1548
1564
1549 def header(ui, repo, patch=None):
1565 def header(ui, repo, patch=None):
1550 """Print the header of the topmost or specified patch"""
1566 """Print the header of the topmost or specified patch"""
1551 q = repo.mq
1567 q = repo.mq
1552
1568
1553 if patch:
1569 if patch:
1554 patch = q.lookup(patch)
1570 patch = q.lookup(patch)
1555 else:
1571 else:
1556 if not q.applied:
1572 if not q.applied:
1557 ui.write('No patches applied\n')
1573 ui.write('No patches applied\n')
1558 return
1574 return
1559 patch = q.lookup('qtip')
1575 patch = q.lookup('qtip')
1560 message = repo.mq.readheaders(patch)[0]
1576 message = repo.mq.readheaders(patch)[0]
1561
1577
1562 ui.write('\n'.join(message) + '\n')
1578 ui.write('\n'.join(message) + '\n')
1563
1579
1564 def lastsavename(path):
1580 def lastsavename(path):
1565 (directory, base) = os.path.split(path)
1581 (directory, base) = os.path.split(path)
1566 names = os.listdir(directory)
1582 names = os.listdir(directory)
1567 namere = re.compile("%s.([0-9]+)" % base)
1583 namere = re.compile("%s.([0-9]+)" % base)
1568 maxindex = None
1584 maxindex = None
1569 maxname = None
1585 maxname = None
1570 for f in names:
1586 for f in names:
1571 m = namere.match(f)
1587 m = namere.match(f)
1572 if m:
1588 if m:
1573 index = int(m.group(1))
1589 index = int(m.group(1))
1574 if maxindex == None or index > maxindex:
1590 if maxindex == None or index > maxindex:
1575 maxindex = index
1591 maxindex = index
1576 maxname = f
1592 maxname = f
1577 if maxname:
1593 if maxname:
1578 return (os.path.join(directory, maxname), maxindex)
1594 return (os.path.join(directory, maxname), maxindex)
1579 return (None, None)
1595 return (None, None)
1580
1596
1581 def savename(path):
1597 def savename(path):
1582 (last, index) = lastsavename(path)
1598 (last, index) = lastsavename(path)
1583 if last is None:
1599 if last is None:
1584 index = 0
1600 index = 0
1585 newpath = path + ".%d" % (index + 1)
1601 newpath = path + ".%d" % (index + 1)
1586 return newpath
1602 return newpath
1587
1603
1588 def push(ui, repo, patch=None, **opts):
1604 def push(ui, repo, patch=None, **opts):
1589 """push the next patch onto the stack"""
1605 """push the next patch onto the stack"""
1590 q = repo.mq
1606 q = repo.mq
1591 mergeq = None
1607 mergeq = None
1592
1608
1593 if opts['all']:
1609 if opts['all']:
1594 patch = q.series[-1]
1610 patch = q.series[-1]
1595 if opts['merge']:
1611 if opts['merge']:
1596 if opts['name']:
1612 if opts['name']:
1597 newpath = opts['name']
1613 newpath = opts['name']
1598 else:
1614 else:
1599 newpath, i = lastsavename(q.path)
1615 newpath, i = lastsavename(q.path)
1600 if not newpath:
1616 if not newpath:
1601 ui.warn("no saved queues found, please use -n\n")
1617 ui.warn("no saved queues found, please use -n\n")
1602 return 1
1618 return 1
1603 mergeq = queue(ui, repo.join(""), newpath)
1619 mergeq = queue(ui, repo.join(""), newpath)
1604 ui.warn("merging with queue at: %s\n" % mergeq.path)
1620 ui.warn("merging with queue at: %s\n" % mergeq.path)
1605 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1621 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1606 mergeq=mergeq)
1622 mergeq=mergeq)
1607 q.save_dirty()
1623 q.save_dirty()
1608 return ret
1624 return ret
1609
1625
1610 def pop(ui, repo, patch=None, **opts):
1626 def pop(ui, repo, patch=None, **opts):
1611 """pop the current patch off the stack"""
1627 """pop the current patch off the stack"""
1612 localupdate = True
1628 localupdate = True
1613 if opts['name']:
1629 if opts['name']:
1614 q = queue(ui, repo.join(""), repo.join(opts['name']))
1630 q = queue(ui, repo.join(""), repo.join(opts['name']))
1615 ui.warn('using patch queue: %s\n' % q.path)
1631 ui.warn('using patch queue: %s\n' % q.path)
1616 localupdate = False
1632 localupdate = False
1617 else:
1633 else:
1618 q = repo.mq
1634 q = repo.mq
1619 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1635 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1620 q.save_dirty()
1636 q.save_dirty()
1621 return 0
1637 return 0
1622
1638
1623 def rename(ui, repo, patch, name=None, **opts):
1639 def rename(ui, repo, patch, name=None, **opts):
1624 """rename a patch
1640 """rename a patch
1625
1641
1626 With one argument, renames the current patch to PATCH1.
1642 With one argument, renames the current patch to PATCH1.
1627 With two arguments, renames PATCH1 to PATCH2."""
1643 With two arguments, renames PATCH1 to PATCH2."""
1628
1644
1629 q = repo.mq
1645 q = repo.mq
1630
1646
1631 if not name:
1647 if not name:
1632 name = patch
1648 name = patch
1633 patch = None
1649 patch = None
1634
1650
1635 if name in q.series:
1651 if name in q.series:
1636 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1652 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1637
1653
1638 absdest = q.join(name)
1654 absdest = q.join(name)
1639 if os.path.exists(absdest):
1655 if os.path.exists(absdest):
1640 raise util.Abort(_('%s already exists') % absdest)
1656 raise util.Abort(_('%s already exists') % absdest)
1641
1657
1642 if patch:
1658 if patch:
1643 patch = q.lookup(patch)
1659 patch = q.lookup(patch)
1644 else:
1660 else:
1645 if not q.applied:
1661 if not q.applied:
1646 ui.write(_('No patches applied\n'))
1662 ui.write(_('No patches applied\n'))
1647 return
1663 return
1648 patch = q.lookup('qtip')
1664 patch = q.lookup('qtip')
1649
1665
1650 if ui.verbose:
1666 if ui.verbose:
1651 ui.write('Renaming %s to %s\n' % (patch, name))
1667 ui.write('Renaming %s to %s\n' % (patch, name))
1652 i = q.find_series(patch)
1668 i = q.find_series(patch)
1653 q.full_series[i] = name
1669 q.full_series[i] = name
1654 q.parse_series()
1670 q.parse_series()
1655 q.series_dirty = 1
1671 q.series_dirty = 1
1656
1672
1657 info = q.isapplied(patch)
1673 info = q.isapplied(patch)
1658 if info:
1674 if info:
1659 q.applied[info[0]] = statusentry(info[1], name)
1675 q.applied[info[0]] = statusentry(info[1], name)
1660 q.applied_dirty = 1
1676 q.applied_dirty = 1
1661
1677
1662 util.rename(q.join(patch), absdest)
1678 util.rename(q.join(patch), absdest)
1663 r = q.qrepo()
1679 r = q.qrepo()
1664 if r:
1680 if r:
1665 wlock = r.wlock()
1681 wlock = r.wlock()
1666 if r.dirstate.state(name) == 'r':
1682 if r.dirstate.state(name) == 'r':
1667 r.undelete([name], wlock)
1683 r.undelete([name], wlock)
1668 r.copy(patch, name, wlock)
1684 r.copy(patch, name, wlock)
1669 r.remove([patch], False, wlock)
1685 r.remove([patch], False, wlock)
1670
1686
1671 q.save_dirty()
1687 q.save_dirty()
1672
1688
1673 def restore(ui, repo, rev, **opts):
1689 def restore(ui, repo, rev, **opts):
1674 """restore the queue state saved by a rev"""
1690 """restore the queue state saved by a rev"""
1675 rev = repo.lookup(rev)
1691 rev = repo.lookup(rev)
1676 q = repo.mq
1692 q = repo.mq
1677 q.restore(repo, rev, delete=opts['delete'],
1693 q.restore(repo, rev, delete=opts['delete'],
1678 qupdate=opts['update'])
1694 qupdate=opts['update'])
1679 q.save_dirty()
1695 q.save_dirty()
1680 return 0
1696 return 0
1681
1697
1682 def save(ui, repo, **opts):
1698 def save(ui, repo, **opts):
1683 """save current queue state"""
1699 """save current queue state"""
1684 q = repo.mq
1700 q = repo.mq
1685 message = commands.logmessage(opts)
1701 message = commands.logmessage(opts)
1686 ret = q.save(repo, msg=message)
1702 ret = q.save(repo, msg=message)
1687 if ret:
1703 if ret:
1688 return ret
1704 return ret
1689 q.save_dirty()
1705 q.save_dirty()
1690 if opts['copy']:
1706 if opts['copy']:
1691 path = q.path
1707 path = q.path
1692 if opts['name']:
1708 if opts['name']:
1693 newpath = os.path.join(q.basepath, opts['name'])
1709 newpath = os.path.join(q.basepath, opts['name'])
1694 if os.path.exists(newpath):
1710 if os.path.exists(newpath):
1695 if not os.path.isdir(newpath):
1711 if not os.path.isdir(newpath):
1696 raise util.Abort(_('destination %s exists and is not '
1712 raise util.Abort(_('destination %s exists and is not '
1697 'a directory') % newpath)
1713 'a directory') % newpath)
1698 if not opts['force']:
1714 if not opts['force']:
1699 raise util.Abort(_('destination %s exists, '
1715 raise util.Abort(_('destination %s exists, '
1700 'use -f to force') % newpath)
1716 'use -f to force') % newpath)
1701 else:
1717 else:
1702 newpath = savename(path)
1718 newpath = savename(path)
1703 ui.warn("copy %s to %s\n" % (path, newpath))
1719 ui.warn("copy %s to %s\n" % (path, newpath))
1704 util.copyfiles(path, newpath)
1720 util.copyfiles(path, newpath)
1705 if opts['empty']:
1721 if opts['empty']:
1706 try:
1722 try:
1707 os.unlink(q.join(q.status_path))
1723 os.unlink(q.join(q.status_path))
1708 except:
1724 except:
1709 pass
1725 pass
1710 return 0
1726 return 0
1711
1727
1712 def strip(ui, repo, rev, **opts):
1728 def strip(ui, repo, rev, **opts):
1713 """strip a revision and all later revs on the same branch"""
1729 """strip a revision and all later revs on the same branch"""
1714 rev = repo.lookup(rev)
1730 rev = repo.lookup(rev)
1715 backup = 'all'
1731 backup = 'all'
1716 if opts['backup']:
1732 if opts['backup']:
1717 backup = 'strip'
1733 backup = 'strip'
1718 elif opts['nobackup']:
1734 elif opts['nobackup']:
1719 backup = 'none'
1735 backup = 'none'
1720 repo.mq.strip(repo, rev, backup=backup)
1736 repo.mq.strip(repo, rev, backup=backup)
1721 return 0
1737 return 0
1722
1738
1723 def select(ui, repo, *args, **opts):
1739 def select(ui, repo, *args, **opts):
1724 '''set or print guarded patches to push
1740 '''set or print guarded patches to push
1725
1741
1726 use qguard command to set or print guards on patch. then use
1742 Use the qguard command to set or print guards on patch, then use
1727 qselect to tell mq which guards to use. example:
1743 qselect to tell mq which guards to use. A patch will be pushed if it
1744 has no guards or any positive guards match the currently selected guard,
1745 but will not be pushed if any negative guards match the current guard.
1746 For example:
1728
1747
1729 qguard foo.patch -stable (nagative guard)
1748 qguard foo.patch -stable (negative guard)
1730 qguard bar.patch +stable (posative guard)
1749 qguard bar.patch +stable (positive guard)
1731 qselect stable
1750 qselect stable
1732
1751
1733 this sets "stable" guard. mq will skip foo.patch (because it has
1752 This activates the "stable" guard. mq will skip foo.patch (because
1734 nagative match) but push bar.patch (because it has posative
1753 it has a negative match) but push bar.patch (because it
1735 match). patch is pushed if any posative guards match and no
1754 has a positive match).
1736 nagative guards match.
1737
1755
1738 with no arguments, default is to print current active guards.
1756 With no arguments, prints the currently active guards.
1739 with arguments, set active guards as given.
1757 With one argument, sets the active guard.
1740
1758
1741 use -n/--none to deactivate guards (no other arguments needed).
1759 Use -n/--none to deactivate guards (no other arguments needed).
1742 when no guards active, patches with posative guards are skipped,
1760 When no guards are active, patches with positive guards are skipped
1743 patches with nagative guards are pushed.
1761 and patches with negative guards are pushed.
1744
1762
1745 qselect can change guards of applied patches. it does not pop
1763 qselect can change the guards on applied patches. It does not pop
1746 guarded patches by default. use --pop to pop back to last applied
1764 guarded patches by default. Use --pop to pop back to the last applied
1747 patch that is not guarded. use --reapply (implies --pop) to push
1765 patch that is not guarded. Use --reapply (which implies --pop) to push
1748 back to current patch afterwards, but skip guarded patches.
1766 back to the current patch afterwards, but skip guarded patches.
1749
1767
1750 use -s/--series to print list of all guards in series file (no
1768 Use -s/--series to print a list of all guards in the series file (no
1751 other arguments needed). use -v for more information.'''
1769 other arguments needed). Use -v for more information.'''
1752
1770
1753 q = repo.mq
1771 q = repo.mq
1754 guards = q.active()
1772 guards = q.active()
1755 if args or opts['none']:
1773 if args or opts['none']:
1756 old_unapplied = q.unapplied(repo)
1774 old_unapplied = q.unapplied(repo)
1757 old_guarded = [i for i in xrange(len(q.applied)) if
1775 old_guarded = [i for i in xrange(len(q.applied)) if
1758 not q.pushable(i)[0]]
1776 not q.pushable(i)[0]]
1759 q.set_active(args)
1777 q.set_active(args)
1760 q.save_dirty()
1778 q.save_dirty()
1761 if not args:
1779 if not args:
1762 ui.status(_('guards deactivated\n'))
1780 ui.status(_('guards deactivated\n'))
1763 if not opts['pop'] and not opts['reapply']:
1781 if not opts['pop'] and not opts['reapply']:
1764 unapplied = q.unapplied(repo)
1782 unapplied = q.unapplied(repo)
1765 guarded = [i for i in xrange(len(q.applied))
1783 guarded = [i for i in xrange(len(q.applied))
1766 if not q.pushable(i)[0]]
1784 if not q.pushable(i)[0]]
1767 if len(unapplied) != len(old_unapplied):
1785 if len(unapplied) != len(old_unapplied):
1768 ui.status(_('number of unguarded, unapplied patches has '
1786 ui.status(_('number of unguarded, unapplied patches has '
1769 'changed from %d to %d\n') %
1787 'changed from %d to %d\n') %
1770 (len(old_unapplied), len(unapplied)))
1788 (len(old_unapplied), len(unapplied)))
1771 if len(guarded) != len(old_guarded):
1789 if len(guarded) != len(old_guarded):
1772 ui.status(_('number of guarded, applied patches has changed '
1790 ui.status(_('number of guarded, applied patches has changed '
1773 'from %d to %d\n') %
1791 'from %d to %d\n') %
1774 (len(old_guarded), len(guarded)))
1792 (len(old_guarded), len(guarded)))
1775 elif opts['series']:
1793 elif opts['series']:
1776 guards = {}
1794 guards = {}
1777 noguards = 0
1795 noguards = 0
1778 for gs in q.series_guards:
1796 for gs in q.series_guards:
1779 if not gs:
1797 if not gs:
1780 noguards += 1
1798 noguards += 1
1781 for g in gs:
1799 for g in gs:
1782 guards.setdefault(g, 0)
1800 guards.setdefault(g, 0)
1783 guards[g] += 1
1801 guards[g] += 1
1784 if ui.verbose:
1802 if ui.verbose:
1785 guards['NONE'] = noguards
1803 guards['NONE'] = noguards
1786 guards = guards.items()
1804 guards = guards.items()
1787 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1805 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1788 if guards:
1806 if guards:
1789 ui.note(_('guards in series file:\n'))
1807 ui.note(_('guards in series file:\n'))
1790 for guard, count in guards:
1808 for guard, count in guards:
1791 ui.note('%2d ' % count)
1809 ui.note('%2d ' % count)
1792 ui.write(guard, '\n')
1810 ui.write(guard, '\n')
1793 else:
1811 else:
1794 ui.note(_('no guards in series file\n'))
1812 ui.note(_('no guards in series file\n'))
1795 else:
1813 else:
1796 if guards:
1814 if guards:
1797 ui.note(_('active guards:\n'))
1815 ui.note(_('active guards:\n'))
1798 for g in guards:
1816 for g in guards:
1799 ui.write(g, '\n')
1817 ui.write(g, '\n')
1800 else:
1818 else:
1801 ui.write(_('no active guards\n'))
1819 ui.write(_('no active guards\n'))
1802 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1820 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1803 popped = False
1821 popped = False
1804 if opts['pop'] or opts['reapply']:
1822 if opts['pop'] or opts['reapply']:
1805 for i in xrange(len(q.applied)):
1823 for i in xrange(len(q.applied)):
1806 pushable, reason = q.pushable(i)
1824 pushable, reason = q.pushable(i)
1807 if not pushable:
1825 if not pushable:
1808 ui.status(_('popping guarded patches\n'))
1826 ui.status(_('popping guarded patches\n'))
1809 popped = True
1827 popped = True
1810 if i == 0:
1828 if i == 0:
1811 q.pop(repo, all=True)
1829 q.pop(repo, all=True)
1812 else:
1830 else:
1813 q.pop(repo, i-1)
1831 q.pop(repo, i-1)
1814 break
1832 break
1815 if popped:
1833 if popped:
1816 try:
1834 try:
1817 if reapply:
1835 if reapply:
1818 ui.status(_('reapplying unguarded patches\n'))
1836 ui.status(_('reapplying unguarded patches\n'))
1819 q.push(repo, reapply)
1837 q.push(repo, reapply)
1820 finally:
1838 finally:
1821 q.save_dirty()
1839 q.save_dirty()
1822
1840
1823 def reposetup(ui, repo):
1841 def reposetup(ui, repo):
1824 class mqrepo(repo.__class__):
1842 class mqrepo(repo.__class__):
1825 def abort_if_wdir_patched(self, errmsg, force=False):
1843 def abort_if_wdir_patched(self, errmsg, force=False):
1826 if self.mq.applied and not force:
1844 if self.mq.applied and not force:
1827 parent = revlog.hex(self.dirstate.parents()[0])
1845 parent = revlog.hex(self.dirstate.parents()[0])
1828 if parent in [s.rev for s in self.mq.applied]:
1846 if parent in [s.rev for s in self.mq.applied]:
1829 raise util.Abort(errmsg)
1847 raise util.Abort(errmsg)
1830
1848
1831 def commit(self, *args, **opts):
1849 def commit(self, *args, **opts):
1832 if len(args) >= 6:
1850 if len(args) >= 6:
1833 force = args[5]
1851 force = args[5]
1834 else:
1852 else:
1835 force = opts.get('force')
1853 force = opts.get('force')
1836 self.abort_if_wdir_patched(
1854 self.abort_if_wdir_patched(
1837 _('cannot commit over an applied mq patch'),
1855 _('cannot commit over an applied mq patch'),
1838 force)
1856 force)
1839
1857
1840 return super(mqrepo, self).commit(*args, **opts)
1858 return super(mqrepo, self).commit(*args, **opts)
1841
1859
1842 def push(self, remote, force=False, revs=None):
1860 def push(self, remote, force=False, revs=None):
1843 if self.mq.applied and not force:
1861 if self.mq.applied and not force:
1844 raise util.Abort(_('source has mq patches applied'))
1862 raise util.Abort(_('source has mq patches applied'))
1845 return super(mqrepo, self).push(remote, force, revs)
1863 return super(mqrepo, self).push(remote, force, revs)
1846
1864
1847 def tags(self):
1865 def tags(self):
1848 if self.tagscache:
1866 if self.tagscache:
1849 return self.tagscache
1867 return self.tagscache
1850
1868
1851 tagscache = super(mqrepo, self).tags()
1869 tagscache = super(mqrepo, self).tags()
1852
1870
1853 q = self.mq
1871 q = self.mq
1854 if not q.applied:
1872 if not q.applied:
1855 return tagscache
1873 return tagscache
1856
1874
1857 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1875 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1858 mqtags.append((mqtags[-1][0], 'qtip'))
1876 mqtags.append((mqtags[-1][0], 'qtip'))
1859 mqtags.append((mqtags[0][0], 'qbase'))
1877 mqtags.append((mqtags[0][0], 'qbase'))
1860 for patch in mqtags:
1878 for patch in mqtags:
1861 if patch[1] in tagscache:
1879 if patch[1] in tagscache:
1862 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1880 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1863 else:
1881 else:
1864 tagscache[patch[1]] = revlog.bin(patch[0])
1882 tagscache[patch[1]] = revlog.bin(patch[0])
1865
1883
1866 return tagscache
1884 return tagscache
1867
1885
1868 if repo.local():
1886 if repo.local():
1869 repo.__class__ = mqrepo
1887 repo.__class__ = mqrepo
1870 repo.mq = queue(ui, repo.join(""))
1888 repo.mq = queue(ui, repo.join(""))
1871
1889
1872 cmdtable = {
1890 cmdtable = {
1873 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1891 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1874 "qclone": (clone,
1892 "qclone": (clone,
1875 [('', 'pull', None, _('use pull protocol to copy metadata')),
1893 [('', 'pull', None, _('use pull protocol to copy metadata')),
1876 ('U', 'noupdate', None, _('do not update the new working directories')),
1894 ('U', 'noupdate', None, _('do not update the new working directories')),
1877 ('', 'uncompressed', None,
1895 ('', 'uncompressed', None,
1878 _('use uncompressed transfer (fast over LAN)')),
1896 _('use uncompressed transfer (fast over LAN)')),
1879 ('e', 'ssh', '', _('specify ssh command to use')),
1897 ('e', 'ssh', '', _('specify ssh command to use')),
1880 ('p', 'patches', '', _('location of source patch repo')),
1898 ('p', 'patches', '', _('location of source patch repo')),
1881 ('', 'remotecmd', '',
1899 ('', 'remotecmd', '',
1882 _('specify hg command to run on the remote side'))],
1900 _('specify hg command to run on the remote side'))],
1883 'hg qclone [OPTION]... SOURCE [DEST]'),
1901 'hg qclone [OPTION]... SOURCE [DEST]'),
1884 "qcommit|qci":
1902 "qcommit|qci":
1885 (commit,
1903 (commit,
1886 commands.table["^commit|ci"][1],
1904 commands.table["^commit|ci"][1],
1887 'hg qcommit [OPTION]... [FILE]...'),
1905 'hg qcommit [OPTION]... [FILE]...'),
1888 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1906 "^qdiff": (diff,
1907 [('I', 'include', [], _('include names matching the given patterns')),
1908 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1909 'hg qdiff [-I] [-X] [FILE]...'),
1889 "qdelete|qremove|qrm":
1910 "qdelete|qremove|qrm":
1890 (delete,
1911 (delete,
1891 [('k', 'keep', None, _('keep patch file'))],
1912 [('k', 'keep', None, _('keep patch file'))],
1892 'hg qdelete [-k] PATCH'),
1913 'hg qdelete [-k] PATCH'),
1893 'qfold':
1914 'qfold':
1894 (fold,
1915 (fold,
1895 [('e', 'edit', None, _('edit patch header')),
1916 [('e', 'edit', None, _('edit patch header')),
1896 ('k', 'keep', None, _('keep folded patch files')),
1917 ('k', 'keep', None, _('keep folded patch files')),
1897 ('m', 'message', '', _('set patch header to <text>')),
1918 ('m', 'message', '', _('set patch header to <text>')),
1898 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1919 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1899 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1920 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1900 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1921 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1901 ('n', 'none', None, _('drop all guards'))],
1922 ('n', 'none', None, _('drop all guards'))],
1902 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1923 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1903 'qheader': (header, [],
1924 'qheader': (header, [],
1904 _('hg qheader [PATCH]')),
1925 _('hg qheader [PATCH]')),
1905 "^qimport":
1926 "^qimport":
1906 (qimport,
1927 (qimport,
1907 [('e', 'existing', None, 'import file in patch dir'),
1928 [('e', 'existing', None, 'import file in patch dir'),
1908 ('n', 'name', '', 'patch file name'),
1929 ('n', 'name', '', 'patch file name'),
1909 ('f', 'force', None, 'overwrite existing files')],
1930 ('f', 'force', None, 'overwrite existing files')],
1910 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1931 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1911 "^qinit":
1932 "^qinit":
1912 (init,
1933 (init,
1913 [('c', 'create-repo', None, 'create queue repository')],
1934 [('c', 'create-repo', None, 'create queue repository')],
1914 'hg qinit [-c]'),
1935 'hg qinit [-c]'),
1915 "qnew":
1936 "qnew":
1916 (new,
1937 (new,
1917 [('m', 'message', '', _('use <text> as commit message')),
1938 [('e', 'edit', None, _('edit commit message')),
1939 ('m', 'message', '', _('use <text> as commit message')),
1918 ('l', 'logfile', '', _('read the commit message from <file>')),
1940 ('l', 'logfile', '', _('read the commit message from <file>')),
1919 ('f', 'force', None, _('import uncommitted changes into patch'))],
1941 ('f', 'force', None, _('import uncommitted changes into patch'))],
1920 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1942 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
1921 "qnext": (next, [], 'hg qnext'),
1943 "qnext": (next, [], 'hg qnext'),
1922 "qprev": (prev, [], 'hg qprev'),
1944 "qprev": (prev, [], 'hg qprev'),
1923 "^qpop":
1945 "^qpop":
1924 (pop,
1946 (pop,
1925 [('a', 'all', None, 'pop all patches'),
1947 [('a', 'all', None, 'pop all patches'),
1926 ('n', 'name', '', 'queue name to pop'),
1948 ('n', 'name', '', 'queue name to pop'),
1927 ('f', 'force', None, 'forget any local changes')],
1949 ('f', 'force', None, 'forget any local changes')],
1928 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1950 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1929 "^qpush":
1951 "^qpush":
1930 (push,
1952 (push,
1931 [('f', 'force', None, 'apply if the patch has rejects'),
1953 [('f', 'force', None, 'apply if the patch has rejects'),
1932 ('l', 'list', None, 'list patch name in commit text'),
1954 ('l', 'list', None, 'list patch name in commit text'),
1933 ('a', 'all', None, 'apply all patches'),
1955 ('a', 'all', None, 'apply all patches'),
1934 ('m', 'merge', None, 'merge from another queue'),
1956 ('m', 'merge', None, 'merge from another queue'),
1935 ('n', 'name', '', 'merge queue name')],
1957 ('n', 'name', '', 'merge queue name')],
1936 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1958 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1937 "^qrefresh":
1959 "^qrefresh":
1938 (refresh,
1960 (refresh,
1939 [('e', 'edit', None, _('edit commit message')),
1961 [('e', 'edit', None, _('edit commit message')),
1940 ('m', 'message', '', _('change commit message with <text>')),
1962 ('m', 'message', '', _('change commit message with <text>')),
1941 ('l', 'logfile', '', _('change commit message with <file> content')),
1963 ('l', 'logfile', '', _('change commit message with <file> content')),
1942 ('s', 'short', None, 'short refresh')],
1964 ('s', 'short', None, 'short refresh'),
1943 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'),
1965 ('I', 'include', [], _('include names matching the given patterns')),
1966 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1967 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
1944 'qrename|qmv':
1968 'qrename|qmv':
1945 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1969 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1946 "qrestore":
1970 "qrestore":
1947 (restore,
1971 (restore,
1948 [('d', 'delete', None, 'delete save entry'),
1972 [('d', 'delete', None, 'delete save entry'),
1949 ('u', 'update', None, 'update queue working dir')],
1973 ('u', 'update', None, 'update queue working dir')],
1950 'hg qrestore [-d] [-u] REV'),
1974 'hg qrestore [-d] [-u] REV'),
1951 "qsave":
1975 "qsave":
1952 (save,
1976 (save,
1953 [('m', 'message', '', _('use <text> as commit message')),
1977 [('m', 'message', '', _('use <text> as commit message')),
1954 ('l', 'logfile', '', _('read the commit message from <file>')),
1978 ('l', 'logfile', '', _('read the commit message from <file>')),
1955 ('c', 'copy', None, 'copy patch directory'),
1979 ('c', 'copy', None, 'copy patch directory'),
1956 ('n', 'name', '', 'copy directory name'),
1980 ('n', 'name', '', 'copy directory name'),
1957 ('e', 'empty', None, 'clear queue status file'),
1981 ('e', 'empty', None, 'clear queue status file'),
1958 ('f', 'force', None, 'force copy')],
1982 ('f', 'force', None, 'force copy')],
1959 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1983 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1960 "qselect": (select,
1984 "qselect": (select,
1961 [('n', 'none', None, _('disable all guards')),
1985 [('n', 'none', None, _('disable all guards')),
1962 ('s', 'series', None, _('list all guards in series file')),
1986 ('s', 'series', None, _('list all guards in series file')),
1963 ('', 'pop', None,
1987 ('', 'pop', None,
1964 _('pop to before first guarded applied patch')),
1988 _('pop to before first guarded applied patch')),
1965 ('', 'reapply', None, _('pop, then reapply patches'))],
1989 ('', 'reapply', None, _('pop, then reapply patches'))],
1966 'hg qselect [OPTION...] [GUARD...]'),
1990 'hg qselect [OPTION...] [GUARD...]'),
1967 "qseries":
1991 "qseries":
1968 (series,
1992 (series,
1969 [('m', 'missing', None, 'print patches not in series'),
1993 [('m', 'missing', None, 'print patches not in series'),
1970 ('s', 'summary', None, _('print first line of patch header'))],
1994 ('s', 'summary', None, _('print first line of patch header'))],
1971 'hg qseries [-m]'),
1995 'hg qseries [-m]'),
1972 "^strip":
1996 "^strip":
1973 (strip,
1997 (strip,
1974 [('f', 'force', None, 'force multi-head removal'),
1998 [('f', 'force', None, 'force multi-head removal'),
1975 ('b', 'backup', None, 'bundle unrelated changesets'),
1999 ('b', 'backup', None, 'bundle unrelated changesets'),
1976 ('n', 'nobackup', None, 'no backups')],
2000 ('n', 'nobackup', None, 'no backups')],
1977 'hg strip [-f] [-b] [-n] REV'),
2001 'hg strip [-f] [-b] [-n] REV'),
1978 "qtop": (top, [], 'hg qtop'),
2002 "qtop": (top, [], 'hg qtop'),
1979 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
2003 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1980 }
2004 }
@@ -1,3522 +1,3489 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival cStringIO changegroup")
15 demandload(globals(), "archival cStringIO changegroup")
16 demandload(globals(), "cmdutil hgweb.server sshserver")
16 demandload(globals(), "cmdutil hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table."""
19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception):
20 class AmbiguousCommand(Exception):
21 """Exception raised if command shortcut matches more than one command."""
21 """Exception raised if command shortcut matches more than one command."""
22
22
23 def bail_if_changed(repo):
23 def bail_if_changed(repo):
24 modified, added, removed, deleted = repo.status()[:4]
24 modified, added, removed, deleted = repo.status()[:4]
25 if modified or added or removed or deleted:
25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes"))
26 raise util.Abort(_("outstanding uncommitted changes"))
27
27
28 def relpath(repo, args):
28 def relpath(repo, args):
29 cwd = repo.getcwd()
29 cwd = repo.getcwd()
30 if cwd:
30 if cwd:
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 return args
32 return args
33
33
34 def logmessage(opts):
34 def logmessage(opts):
35 """ get the log message according to -m and -l option """
35 """ get the log message according to -m and -l option """
36 message = opts['message']
36 message = opts['message']
37 logfile = opts['logfile']
37 logfile = opts['logfile']
38
38
39 if message and logfile:
39 if message and logfile:
40 raise util.Abort(_('options --message and --logfile are mutually '
40 raise util.Abort(_('options --message and --logfile are mutually '
41 'exclusive'))
41 'exclusive'))
42 if not message and logfile:
42 if not message and logfile:
43 try:
43 try:
44 if logfile == '-':
44 if logfile == '-':
45 message = sys.stdin.read()
45 message = sys.stdin.read()
46 else:
46 else:
47 message = open(logfile).read()
47 message = open(logfile).read()
48 except IOError, inst:
48 except IOError, inst:
49 raise util.Abort(_("can't read commit message '%s': %s") %
49 raise util.Abort(_("can't read commit message '%s': %s") %
50 (logfile, inst.strerror))
50 (logfile, inst.strerror))
51 return message
51 return message
52
52
53 def walkchangerevs(ui, repo, pats, opts):
53 def walkchangerevs(ui, repo, pats, opts):
54 '''Iterate over files and the revs they changed in.
54 '''Iterate over files and the revs they changed in.
55
55
56 Callers most commonly need to iterate backwards over the history
56 Callers most commonly need to iterate backwards over the history
57 it is interested in. Doing so has awful (quadratic-looking)
57 it is interested in. Doing so has awful (quadratic-looking)
58 performance, so we use iterators in a "windowed" way.
58 performance, so we use iterators in a "windowed" way.
59
59
60 We walk a window of revisions in the desired order. Within the
60 We walk a window of revisions in the desired order. Within the
61 window, we first walk forwards to gather data, then in the desired
61 window, we first walk forwards to gather data, then in the desired
62 order (usually backwards) to display it.
62 order (usually backwards) to display it.
63
63
64 This function returns an (iterator, getchange, matchfn) tuple. The
64 This function returns an (iterator, getchange, matchfn) tuple. The
65 getchange function returns the changelog entry for a numeric
65 getchange function returns the changelog entry for a numeric
66 revision. The iterator yields 3-tuples. They will be of one of
66 revision. The iterator yields 3-tuples. They will be of one of
67 the following forms:
67 the following forms:
68
68
69 "window", incrementing, lastrev: stepping through a window,
69 "window", incrementing, lastrev: stepping through a window,
70 positive if walking forwards through revs, last rev in the
70 positive if walking forwards through revs, last rev in the
71 sequence iterated over - use to reset state for the current window
71 sequence iterated over - use to reset state for the current window
72
72
73 "add", rev, fns: out-of-order traversal of the given file names
73 "add", rev, fns: out-of-order traversal of the given file names
74 fns, which changed during revision rev - use to gather data for
74 fns, which changed during revision rev - use to gather data for
75 possible display
75 possible display
76
76
77 "iter", rev, None: in-order traversal of the revs earlier iterated
77 "iter", rev, None: in-order traversal of the revs earlier iterated
78 over with "add" - use to display data'''
78 over with "add" - use to display data'''
79
79
80 def increasing_windows(start, end, windowsize=8, sizelimit=512):
80 def increasing_windows(start, end, windowsize=8, sizelimit=512):
81 if start < end:
81 if start < end:
82 while start < end:
82 while start < end:
83 yield start, min(windowsize, end-start)
83 yield start, min(windowsize, end-start)
84 start += windowsize
84 start += windowsize
85 if windowsize < sizelimit:
85 if windowsize < sizelimit:
86 windowsize *= 2
86 windowsize *= 2
87 else:
87 else:
88 while start > end:
88 while start > end:
89 yield start, min(windowsize, start-end-1)
89 yield start, min(windowsize, start-end-1)
90 start -= windowsize
90 start -= windowsize
91 if windowsize < sizelimit:
91 if windowsize < sizelimit:
92 windowsize *= 2
92 windowsize *= 2
93
93
94
94
95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 follow = opts.get('follow') or opts.get('follow_first')
96 follow = opts.get('follow') or opts.get('follow_first')
97
97
98 if repo.changelog.count() == 0:
98 if repo.changelog.count() == 0:
99 return [], False, matchfn
99 return [], False, matchfn
100
100
101 if follow:
101 if follow:
102 p = repo.dirstate.parents()[0]
102 p = repo.dirstate.parents()[0]
103 if p == nullid:
103 if p == nullid:
104 ui.warn(_('No working directory revision; defaulting to tip\n'))
104 ui.warn(_('No working directory revision; defaulting to tip\n'))
105 start = 'tip'
105 start = 'tip'
106 else:
106 else:
107 start = repo.changelog.rev(p)
107 start = repo.changelog.rev(p)
108 defrange = '%s:0' % start
108 defrange = '%s:0' % start
109 else:
109 else:
110 defrange = 'tip:0'
110 defrange = 'tip:0'
111 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
111 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
112 wanted = {}
112 wanted = {}
113 slowpath = anypats
113 slowpath = anypats
114 fncache = {}
114 fncache = {}
115
115
116 chcache = {}
116 chcache = {}
117 def getchange(rev):
117 def getchange(rev):
118 ch = chcache.get(rev)
118 ch = chcache.get(rev)
119 if ch is None:
119 if ch is None:
120 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
120 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
121 return ch
121 return ch
122
122
123 if not slowpath and not files:
123 if not slowpath and not files:
124 # No files, no patterns. Display all revs.
124 # No files, no patterns. Display all revs.
125 wanted = dict(zip(revs, revs))
125 wanted = dict(zip(revs, revs))
126 copies = []
126 copies = []
127 if not slowpath:
127 if not slowpath:
128 # Only files, no patterns. Check the history of each file.
128 # Only files, no patterns. Check the history of each file.
129 def filerevgen(filelog, node):
129 def filerevgen(filelog, node):
130 cl_count = repo.changelog.count()
130 cl_count = repo.changelog.count()
131 if node is None:
131 if node is None:
132 last = filelog.count() - 1
132 last = filelog.count() - 1
133 else:
133 else:
134 last = filelog.rev(node)
134 last = filelog.rev(node)
135 for i, window in increasing_windows(last, -1):
135 for i, window in increasing_windows(last, -1):
136 revs = []
136 revs = []
137 for j in xrange(i - window, i + 1):
137 for j in xrange(i - window, i + 1):
138 n = filelog.node(j)
138 n = filelog.node(j)
139 revs.append((filelog.linkrev(n),
139 revs.append((filelog.linkrev(n),
140 follow and filelog.renamed(n)))
140 follow and filelog.renamed(n)))
141 revs.reverse()
141 revs.reverse()
142 for rev in revs:
142 for rev in revs:
143 # only yield rev for which we have the changelog, it can
143 # only yield rev for which we have the changelog, it can
144 # happen while doing "hg log" during a pull or commit
144 # happen while doing "hg log" during a pull or commit
145 if rev[0] < cl_count:
145 if rev[0] < cl_count:
146 yield rev
146 yield rev
147 def iterfiles():
147 def iterfiles():
148 for filename in files:
148 for filename in files:
149 yield filename, None
149 yield filename, None
150 for filename_node in copies:
150 for filename_node in copies:
151 yield filename_node
151 yield filename_node
152 minrev, maxrev = min(revs), max(revs)
152 minrev, maxrev = min(revs), max(revs)
153 for file_, node in iterfiles():
153 for file_, node in iterfiles():
154 filelog = repo.file(file_)
154 filelog = repo.file(file_)
155 # A zero count may be a directory or deleted file, so
155 # A zero count may be a directory or deleted file, so
156 # try to find matching entries on the slow path.
156 # try to find matching entries on the slow path.
157 if filelog.count() == 0:
157 if filelog.count() == 0:
158 slowpath = True
158 slowpath = True
159 break
159 break
160 for rev, copied in filerevgen(filelog, node):
160 for rev, copied in filerevgen(filelog, node):
161 if rev <= maxrev:
161 if rev <= maxrev:
162 if rev < minrev:
162 if rev < minrev:
163 break
163 break
164 fncache.setdefault(rev, [])
164 fncache.setdefault(rev, [])
165 fncache[rev].append(file_)
165 fncache[rev].append(file_)
166 wanted[rev] = 1
166 wanted[rev] = 1
167 if follow and copied:
167 if follow and copied:
168 copies.append(copied)
168 copies.append(copied)
169 if slowpath:
169 if slowpath:
170 if follow:
170 if follow:
171 raise util.Abort(_('can only follow copies/renames for explicit '
171 raise util.Abort(_('can only follow copies/renames for explicit '
172 'file names'))
172 'file names'))
173
173
174 # The slow path checks files modified in every changeset.
174 # The slow path checks files modified in every changeset.
175 def changerevgen():
175 def changerevgen():
176 for i, window in increasing_windows(repo.changelog.count()-1, -1):
176 for i, window in increasing_windows(repo.changelog.count()-1, -1):
177 for j in xrange(i - window, i + 1):
177 for j in xrange(i - window, i + 1):
178 yield j, getchange(j)[3]
178 yield j, getchange(j)[3]
179
179
180 for rev, changefiles in changerevgen():
180 for rev, changefiles in changerevgen():
181 matches = filter(matchfn, changefiles)
181 matches = filter(matchfn, changefiles)
182 if matches:
182 if matches:
183 fncache[rev] = matches
183 fncache[rev] = matches
184 wanted[rev] = 1
184 wanted[rev] = 1
185
185
186 class followfilter:
186 class followfilter:
187 def __init__(self, onlyfirst=False):
187 def __init__(self, onlyfirst=False):
188 self.startrev = -1
188 self.startrev = -1
189 self.roots = []
189 self.roots = []
190 self.onlyfirst = onlyfirst
190 self.onlyfirst = onlyfirst
191
191
192 def match(self, rev):
192 def match(self, rev):
193 def realparents(rev):
193 def realparents(rev):
194 if self.onlyfirst:
194 if self.onlyfirst:
195 return repo.changelog.parentrevs(rev)[0:1]
195 return repo.changelog.parentrevs(rev)[0:1]
196 else:
196 else:
197 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
197 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
198
198
199 if self.startrev == -1:
199 if self.startrev == -1:
200 self.startrev = rev
200 self.startrev = rev
201 return True
201 return True
202
202
203 if rev > self.startrev:
203 if rev > self.startrev:
204 # forward: all descendants
204 # forward: all descendants
205 if not self.roots:
205 if not self.roots:
206 self.roots.append(self.startrev)
206 self.roots.append(self.startrev)
207 for parent in realparents(rev):
207 for parent in realparents(rev):
208 if parent in self.roots:
208 if parent in self.roots:
209 self.roots.append(rev)
209 self.roots.append(rev)
210 return True
210 return True
211 else:
211 else:
212 # backwards: all parents
212 # backwards: all parents
213 if not self.roots:
213 if not self.roots:
214 self.roots.extend(realparents(self.startrev))
214 self.roots.extend(realparents(self.startrev))
215 if rev in self.roots:
215 if rev in self.roots:
216 self.roots.remove(rev)
216 self.roots.remove(rev)
217 self.roots.extend(realparents(rev))
217 self.roots.extend(realparents(rev))
218 return True
218 return True
219
219
220 return False
220 return False
221
221
222 # it might be worthwhile to do this in the iterator if the rev range
222 # it might be worthwhile to do this in the iterator if the rev range
223 # is descending and the prune args are all within that range
223 # is descending and the prune args are all within that range
224 for rev in opts.get('prune', ()):
224 for rev in opts.get('prune', ()):
225 rev = repo.changelog.rev(repo.lookup(rev))
225 rev = repo.changelog.rev(repo.lookup(rev))
226 ff = followfilter()
226 ff = followfilter()
227 stop = min(revs[0], revs[-1])
227 stop = min(revs[0], revs[-1])
228 for x in range(rev, stop-1, -1):
228 for x in range(rev, stop-1, -1):
229 if ff.match(x) and wanted.has_key(x):
229 if ff.match(x) and wanted.has_key(x):
230 del wanted[x]
230 del wanted[x]
231
231
232 def iterate():
232 def iterate():
233 if follow and not files:
233 if follow and not files:
234 ff = followfilter(onlyfirst=opts.get('follow_first'))
234 ff = followfilter(onlyfirst=opts.get('follow_first'))
235 def want(rev):
235 def want(rev):
236 if ff.match(rev) and rev in wanted:
236 if ff.match(rev) and rev in wanted:
237 return True
237 return True
238 return False
238 return False
239 else:
239 else:
240 def want(rev):
240 def want(rev):
241 return rev in wanted
241 return rev in wanted
242
242
243 for i, window in increasing_windows(0, len(revs)):
243 for i, window in increasing_windows(0, len(revs)):
244 yield 'window', revs[0] < revs[-1], revs[-1]
244 yield 'window', revs[0] < revs[-1], revs[-1]
245 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
245 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
246 srevs = list(nrevs)
246 srevs = list(nrevs)
247 srevs.sort()
247 srevs.sort()
248 for rev in srevs:
248 for rev in srevs:
249 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
249 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
250 yield 'add', rev, fns
250 yield 'add', rev, fns
251 for rev in nrevs:
251 for rev in nrevs:
252 yield 'iter', rev, None
252 yield 'iter', rev, None
253 return iterate(), getchange, matchfn
253 return iterate(), getchange, matchfn
254
254
255 revrangesep = ':'
255 revrangesep = ':'
256
256
257 def revfix(repo, val, defval):
257 def revfix(repo, val, defval):
258 '''turn user-level id of changeset into rev number.
258 '''turn user-level id of changeset into rev number.
259 user-level id can be tag, changeset, rev number, or negative rev
259 user-level id can be tag, changeset, rev number, or negative rev
260 number relative to number of revs (-1 is tip, etc).'''
260 number relative to number of revs (-1 is tip, etc).'''
261 if not val:
261 if not val:
262 return defval
262 return defval
263 try:
263 try:
264 num = int(val)
264 num = int(val)
265 if str(num) != val:
265 if str(num) != val:
266 raise ValueError
266 raise ValueError
267 if num < 0:
267 if num < 0:
268 num += repo.changelog.count()
268 num += repo.changelog.count()
269 if num < 0:
269 if num < 0:
270 num = 0
270 num = 0
271 elif num >= repo.changelog.count():
271 elif num >= repo.changelog.count():
272 raise ValueError
272 raise ValueError
273 except ValueError:
273 except ValueError:
274 try:
274 try:
275 num = repo.changelog.rev(repo.lookup(val))
275 num = repo.changelog.rev(repo.lookup(val))
276 except KeyError:
276 except KeyError:
277 raise util.Abort(_('invalid revision identifier %s'), val)
277 raise util.Abort(_('invalid revision identifier %s'), val)
278 return num
278 return num
279
279
280 def revpair(ui, repo, revs):
280 def revpair(ui, repo, revs):
281 '''return pair of nodes, given list of revisions. second item can
281 '''return pair of nodes, given list of revisions. second item can
282 be None, meaning use working dir.'''
282 be None, meaning use working dir.'''
283 if not revs:
283 if not revs:
284 return repo.dirstate.parents()[0], None
284 return repo.dirstate.parents()[0], None
285 end = None
285 end = None
286 if len(revs) == 1:
286 if len(revs) == 1:
287 start = revs[0]
287 start = revs[0]
288 if revrangesep in start:
288 if revrangesep in start:
289 start, end = start.split(revrangesep, 1)
289 start, end = start.split(revrangesep, 1)
290 start = revfix(repo, start, 0)
290 start = revfix(repo, start, 0)
291 end = revfix(repo, end, repo.changelog.count() - 1)
291 end = revfix(repo, end, repo.changelog.count() - 1)
292 else:
292 else:
293 start = revfix(repo, start, None)
293 start = revfix(repo, start, None)
294 elif len(revs) == 2:
294 elif len(revs) == 2:
295 if revrangesep in revs[0] or revrangesep in revs[1]:
295 if revrangesep in revs[0] or revrangesep in revs[1]:
296 raise util.Abort(_('too many revisions specified'))
296 raise util.Abort(_('too many revisions specified'))
297 start = revfix(repo, revs[0], None)
297 start = revfix(repo, revs[0], None)
298 end = revfix(repo, revs[1], None)
298 end = revfix(repo, revs[1], None)
299 else:
299 else:
300 raise util.Abort(_('too many revisions specified'))
300 raise util.Abort(_('too many revisions specified'))
301 if end is not None: end = repo.lookup(str(end))
301 if end is not None: end = repo.lookup(str(end))
302 return repo.lookup(str(start)), end
302 return repo.lookup(str(start)), end
303
303
304 def revrange(ui, repo, revs):
304 def revrange(ui, repo, revs):
305 """Yield revision as strings from a list of revision specifications."""
305 """Yield revision as strings from a list of revision specifications."""
306 seen = {}
306 seen = {}
307 for spec in revs:
307 for spec in revs:
308 if revrangesep in spec:
308 if revrangesep in spec:
309 start, end = spec.split(revrangesep, 1)
309 start, end = spec.split(revrangesep, 1)
310 start = revfix(repo, start, 0)
310 start = revfix(repo, start, 0)
311 end = revfix(repo, end, repo.changelog.count() - 1)
311 end = revfix(repo, end, repo.changelog.count() - 1)
312 step = start > end and -1 or 1
312 step = start > end and -1 or 1
313 for rev in xrange(start, end+step, step):
313 for rev in xrange(start, end+step, step):
314 if rev in seen:
314 if rev in seen:
315 continue
315 continue
316 seen[rev] = 1
316 seen[rev] = 1
317 yield str(rev)
317 yield str(rev)
318 else:
318 else:
319 rev = revfix(repo, spec, None)
319 rev = revfix(repo, spec, None)
320 if rev in seen:
320 if rev in seen:
321 continue
321 continue
322 seen[rev] = 1
322 seen[rev] = 1
323 yield str(rev)
323 yield str(rev)
324
324
325 def write_bundle(cg, filename=None, compress=True):
325 def write_bundle(cg, filename=None, compress=True):
326 """Write a bundle file and return its filename.
326 """Write a bundle file and return its filename.
327
327
328 Existing files will not be overwritten.
328 Existing files will not be overwritten.
329 If no filename is specified, a temporary file is created.
329 If no filename is specified, a temporary file is created.
330 bz2 compression can be turned off.
330 bz2 compression can be turned off.
331 The bundle file will be deleted in case of errors.
331 The bundle file will be deleted in case of errors.
332 """
332 """
333 class nocompress(object):
333 class nocompress(object):
334 def compress(self, x):
334 def compress(self, x):
335 return x
335 return x
336 def flush(self):
336 def flush(self):
337 return ""
337 return ""
338
338
339 fh = None
339 fh = None
340 cleanup = None
340 cleanup = None
341 try:
341 try:
342 if filename:
342 if filename:
343 if os.path.exists(filename):
343 if os.path.exists(filename):
344 raise util.Abort(_("file '%s' already exists"), filename)
344 raise util.Abort(_("file '%s' already exists"), filename)
345 fh = open(filename, "wb")
345 fh = open(filename, "wb")
346 else:
346 else:
347 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
347 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
348 fh = os.fdopen(fd, "wb")
348 fh = os.fdopen(fd, "wb")
349 cleanup = filename
349 cleanup = filename
350
350
351 if compress:
351 if compress:
352 fh.write("HG10")
352 fh.write("HG10")
353 z = bz2.BZ2Compressor(9)
353 z = bz2.BZ2Compressor(9)
354 else:
354 else:
355 fh.write("HG10UN")
355 fh.write("HG10UN")
356 z = nocompress()
356 z = nocompress()
357 # parse the changegroup data, otherwise we will block
357 # parse the changegroup data, otherwise we will block
358 # in case of sshrepo because we don't know the end of the stream
358 # in case of sshrepo because we don't know the end of the stream
359
359
360 # an empty chunkiter is the end of the changegroup
360 # an empty chunkiter is the end of the changegroup
361 empty = False
361 empty = False
362 while not empty:
362 while not empty:
363 empty = True
363 empty = True
364 for chunk in changegroup.chunkiter(cg):
364 for chunk in changegroup.chunkiter(cg):
365 empty = False
365 empty = False
366 fh.write(z.compress(changegroup.genchunk(chunk)))
366 fh.write(z.compress(changegroup.genchunk(chunk)))
367 fh.write(z.compress(changegroup.closechunk()))
367 fh.write(z.compress(changegroup.closechunk()))
368 fh.write(z.flush())
368 fh.write(z.flush())
369 cleanup = None
369 cleanup = None
370 return filename
370 return filename
371 finally:
371 finally:
372 if fh is not None:
372 if fh is not None:
373 fh.close()
373 fh.close()
374 if cleanup is not None:
374 if cleanup is not None:
375 os.unlink(cleanup)
375 os.unlink(cleanup)
376
376
377 def trimuser(ui, name, rev, revcache):
377 def trimuser(ui, name, rev, revcache):
378 """trim the name of the user who committed a change"""
378 """trim the name of the user who committed a change"""
379 user = revcache.get(rev)
379 user = revcache.get(rev)
380 if user is None:
380 if user is None:
381 user = revcache[rev] = ui.shortuser(name)
381 user = revcache[rev] = ui.shortuser(name)
382 return user
382 return user
383
383
384 class changeset_printer(object):
384 class changeset_printer(object):
385 '''show changeset information when templating not requested.'''
385 '''show changeset information when templating not requested.'''
386
386
387 def __init__(self, ui, repo):
387 def __init__(self, ui, repo):
388 self.ui = ui
388 self.ui = ui
389 self.repo = repo
389 self.repo = repo
390
390
391 def show(self, rev=0, changenode=None, brinfo=None):
391 def show(self, rev=0, changenode=None, brinfo=None):
392 '''show a single changeset or file revision'''
392 '''show a single changeset or file revision'''
393 log = self.repo.changelog
393 log = self.repo.changelog
394 if changenode is None:
394 if changenode is None:
395 changenode = log.node(rev)
395 changenode = log.node(rev)
396 elif not rev:
396 elif not rev:
397 rev = log.rev(changenode)
397 rev = log.rev(changenode)
398
398
399 if self.ui.quiet:
399 if self.ui.quiet:
400 self.ui.write("%d:%s\n" % (rev, short(changenode)))
400 self.ui.write("%d:%s\n" % (rev, short(changenode)))
401 return
401 return
402
402
403 changes = log.read(changenode)
403 changes = log.read(changenode)
404 date = util.datestr(changes[2])
404 date = util.datestr(changes[2])
405
405
406 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
406 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
407 for p in log.parents(changenode)
407 for p in log.parents(changenode)
408 if self.ui.debugflag or p != nullid]
408 if self.ui.debugflag or p != nullid]
409 if (not self.ui.debugflag and len(parents) == 1 and
409 if (not self.ui.debugflag and len(parents) == 1 and
410 parents[0][0] == rev-1):
410 parents[0][0] == rev-1):
411 parents = []
411 parents = []
412
412
413 if self.ui.verbose:
413 if self.ui.verbose:
414 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
414 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
415 else:
415 else:
416 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
416 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
417
417
418 for tag in self.repo.nodetags(changenode):
418 for tag in self.repo.nodetags(changenode):
419 self.ui.status(_("tag: %s\n") % tag)
419 self.ui.status(_("tag: %s\n") % tag)
420 for parent in parents:
420 for parent in parents:
421 self.ui.write(_("parent: %d:%s\n") % parent)
421 self.ui.write(_("parent: %d:%s\n") % parent)
422
422
423 if brinfo and changenode in brinfo:
423 if brinfo and changenode in brinfo:
424 br = brinfo[changenode]
424 br = brinfo[changenode]
425 self.ui.write(_("branch: %s\n") % " ".join(br))
425 self.ui.write(_("branch: %s\n") % " ".join(br))
426
426
427 self.ui.debug(_("manifest: %d:%s\n") %
427 self.ui.debug(_("manifest: %d:%s\n") %
428 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
428 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
429 self.ui.status(_("user: %s\n") % changes[1])
429 self.ui.status(_("user: %s\n") % changes[1])
430 self.ui.status(_("date: %s\n") % date)
430 self.ui.status(_("date: %s\n") % date)
431
431
432 if self.ui.debugflag:
432 if self.ui.debugflag:
433 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
433 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
434 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
434 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
435 files):
435 files):
436 if value:
436 if value:
437 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
437 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
438 else:
438 else:
439 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
439 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
440
440
441 description = changes[4].strip()
441 description = changes[4].strip()
442 if description:
442 if description:
443 if self.ui.verbose:
443 if self.ui.verbose:
444 self.ui.status(_("description:\n"))
444 self.ui.status(_("description:\n"))
445 self.ui.status(description)
445 self.ui.status(description)
446 self.ui.status("\n\n")
446 self.ui.status("\n\n")
447 else:
447 else:
448 self.ui.status(_("summary: %s\n") %
448 self.ui.status(_("summary: %s\n") %
449 description.splitlines()[0])
449 description.splitlines()[0])
450 self.ui.status("\n")
450 self.ui.status("\n")
451
451
452 def show_changeset(ui, repo, opts):
452 def show_changeset(ui, repo, opts):
453 '''show one changeset. uses template or regular display. caller
453 '''show one changeset. uses template or regular display. caller
454 can pass in 'style' and 'template' options in opts.'''
454 can pass in 'style' and 'template' options in opts.'''
455
455
456 tmpl = opts.get('template')
456 tmpl = opts.get('template')
457 if tmpl:
457 if tmpl:
458 tmpl = templater.parsestring(tmpl, quoted=False)
458 tmpl = templater.parsestring(tmpl, quoted=False)
459 else:
459 else:
460 tmpl = ui.config('ui', 'logtemplate')
460 tmpl = ui.config('ui', 'logtemplate')
461 if tmpl: tmpl = templater.parsestring(tmpl)
461 if tmpl: tmpl = templater.parsestring(tmpl)
462 mapfile = opts.get('style') or ui.config('ui', 'style')
462 mapfile = opts.get('style') or ui.config('ui', 'style')
463 if tmpl or mapfile:
463 if tmpl or mapfile:
464 if mapfile:
464 if mapfile:
465 if not os.path.isfile(mapfile):
465 if not os.path.isfile(mapfile):
466 mapname = templater.templatepath('map-cmdline.' + mapfile)
466 mapname = templater.templatepath('map-cmdline.' + mapfile)
467 if not mapname: mapname = templater.templatepath(mapfile)
467 if not mapname: mapname = templater.templatepath(mapfile)
468 if mapname: mapfile = mapname
468 if mapname: mapfile = mapname
469 try:
469 try:
470 t = templater.changeset_templater(ui, repo, mapfile)
470 t = templater.changeset_templater(ui, repo, mapfile)
471 except SyntaxError, inst:
471 except SyntaxError, inst:
472 raise util.Abort(inst.args[0])
472 raise util.Abort(inst.args[0])
473 if tmpl: t.use_template(tmpl)
473 if tmpl: t.use_template(tmpl)
474 return t
474 return t
475 return changeset_printer(ui, repo)
475 return changeset_printer(ui, repo)
476
476
477 def setremoteconfig(ui, opts):
477 def setremoteconfig(ui, opts):
478 "copy remote options to ui tree"
478 "copy remote options to ui tree"
479 if opts.get('ssh'):
479 if opts.get('ssh'):
480 ui.setconfig("ui", "ssh", opts['ssh'])
480 ui.setconfig("ui", "ssh", opts['ssh'])
481 if opts.get('remotecmd'):
481 if opts.get('remotecmd'):
482 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
482 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
483
483
484 def show_version(ui):
484 def show_version(ui):
485 """output version and copyright information"""
485 """output version and copyright information"""
486 ui.write(_("Mercurial Distributed SCM (version %s)\n")
486 ui.write(_("Mercurial Distributed SCM (version %s)\n")
487 % version.get_version())
487 % version.get_version())
488 ui.status(_(
488 ui.status(_(
489 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
489 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
490 "This is free software; see the source for copying conditions. "
490 "This is free software; see the source for copying conditions. "
491 "There is NO\nwarranty; "
491 "There is NO\nwarranty; "
492 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
492 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
493 ))
493 ))
494
494
495 def help_(ui, name=None, with_version=False):
495 def help_(ui, name=None, with_version=False):
496 """show help for a command, extension, or list of commands
496 """show help for a command, extension, or list of commands
497
497
498 With no arguments, print a list of commands and short help.
498 With no arguments, print a list of commands and short help.
499
499
500 Given a command name, print help for that command.
500 Given a command name, print help for that command.
501
501
502 Given an extension name, print help for that extension, and the
502 Given an extension name, print help for that extension, and the
503 commands it provides."""
503 commands it provides."""
504 option_lists = []
504 option_lists = []
505
505
506 def helpcmd(name):
506 def helpcmd(name):
507 if with_version:
507 if with_version:
508 show_version(ui)
508 show_version(ui)
509 ui.write('\n')
509 ui.write('\n')
510 aliases, i = findcmd(name)
510 aliases, i = findcmd(name)
511 # synopsis
511 # synopsis
512 ui.write("%s\n\n" % i[2])
512 ui.write("%s\n\n" % i[2])
513
513
514 # description
514 # description
515 doc = i[0].__doc__
515 doc = i[0].__doc__
516 if not doc:
516 if not doc:
517 doc = _("(No help text available)")
517 doc = _("(No help text available)")
518 if ui.quiet:
518 if ui.quiet:
519 doc = doc.splitlines(0)[0]
519 doc = doc.splitlines(0)[0]
520 ui.write("%s\n" % doc.rstrip())
520 ui.write("%s\n" % doc.rstrip())
521
521
522 if not ui.quiet:
522 if not ui.quiet:
523 # aliases
523 # aliases
524 if len(aliases) > 1:
524 if len(aliases) > 1:
525 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
525 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
526
526
527 # options
527 # options
528 if i[1]:
528 if i[1]:
529 option_lists.append(("options", i[1]))
529 option_lists.append(("options", i[1]))
530
530
531 def helplist(select=None):
531 def helplist(select=None):
532 h = {}
532 h = {}
533 cmds = {}
533 cmds = {}
534 for c, e in table.items():
534 for c, e in table.items():
535 f = c.split("|", 1)[0]
535 f = c.split("|", 1)[0]
536 if select and not select(f):
536 if select and not select(f):
537 continue
537 continue
538 if name == "shortlist" and not f.startswith("^"):
538 if name == "shortlist" and not f.startswith("^"):
539 continue
539 continue
540 f = f.lstrip("^")
540 f = f.lstrip("^")
541 if not ui.debugflag and f.startswith("debug"):
541 if not ui.debugflag and f.startswith("debug"):
542 continue
542 continue
543 doc = e[0].__doc__
543 doc = e[0].__doc__
544 if not doc:
544 if not doc:
545 doc = _("(No help text available)")
545 doc = _("(No help text available)")
546 h[f] = doc.splitlines(0)[0].rstrip()
546 h[f] = doc.splitlines(0)[0].rstrip()
547 cmds[f] = c.lstrip("^")
547 cmds[f] = c.lstrip("^")
548
548
549 fns = h.keys()
549 fns = h.keys()
550 fns.sort()
550 fns.sort()
551 m = max(map(len, fns))
551 m = max(map(len, fns))
552 for f in fns:
552 for f in fns:
553 if ui.verbose:
553 if ui.verbose:
554 commands = cmds[f].replace("|",", ")
554 commands = cmds[f].replace("|",", ")
555 ui.write(" %s:\n %s\n"%(commands, h[f]))
555 ui.write(" %s:\n %s\n"%(commands, h[f]))
556 else:
556 else:
557 ui.write(' %-*s %s\n' % (m, f, h[f]))
557 ui.write(' %-*s %s\n' % (m, f, h[f]))
558
558
559 def helpext(name):
559 def helpext(name):
560 try:
560 try:
561 mod = findext(name)
561 mod = findext(name)
562 except KeyError:
562 except KeyError:
563 raise UnknownCommand(name)
563 raise UnknownCommand(name)
564
564
565 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
565 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
566 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
566 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
567 for d in doc[1:]:
567 for d in doc[1:]:
568 ui.write(d, '\n')
568 ui.write(d, '\n')
569
569
570 ui.status('\n')
570 ui.status('\n')
571 if ui.verbose:
571 if ui.verbose:
572 ui.status(_('list of commands:\n\n'))
572 ui.status(_('list of commands:\n\n'))
573 else:
573 else:
574 ui.status(_('list of commands (use "hg help -v %s" '
574 ui.status(_('list of commands (use "hg help -v %s" '
575 'to show aliases and global options):\n\n') % name)
575 'to show aliases and global options):\n\n') % name)
576
576
577 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
577 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
578 helplist(modcmds.has_key)
578 helplist(modcmds.has_key)
579
579
580 if name and name != 'shortlist':
580 if name and name != 'shortlist':
581 try:
581 try:
582 helpcmd(name)
582 helpcmd(name)
583 except UnknownCommand:
583 except UnknownCommand:
584 helpext(name)
584 helpext(name)
585
585
586 else:
586 else:
587 # program name
587 # program name
588 if ui.verbose or with_version:
588 if ui.verbose or with_version:
589 show_version(ui)
589 show_version(ui)
590 else:
590 else:
591 ui.status(_("Mercurial Distributed SCM\n"))
591 ui.status(_("Mercurial Distributed SCM\n"))
592 ui.status('\n')
592 ui.status('\n')
593
593
594 # list of commands
594 # list of commands
595 if name == "shortlist":
595 if name == "shortlist":
596 ui.status(_('basic commands (use "hg help" '
596 ui.status(_('basic commands (use "hg help" '
597 'for the full list or option "-v" for details):\n\n'))
597 'for the full list or option "-v" for details):\n\n'))
598 elif ui.verbose:
598 elif ui.verbose:
599 ui.status(_('list of commands:\n\n'))
599 ui.status(_('list of commands:\n\n'))
600 else:
600 else:
601 ui.status(_('list of commands (use "hg help -v" '
601 ui.status(_('list of commands (use "hg help -v" '
602 'to show aliases and global options):\n\n'))
602 'to show aliases and global options):\n\n'))
603
603
604 helplist()
604 helplist()
605
605
606 # global options
606 # global options
607 if ui.verbose:
607 if ui.verbose:
608 option_lists.append(("global options", globalopts))
608 option_lists.append(("global options", globalopts))
609
609
610 # list all option lists
610 # list all option lists
611 opt_output = []
611 opt_output = []
612 for title, options in option_lists:
612 for title, options in option_lists:
613 opt_output.append(("\n%s:\n" % title, None))
613 opt_output.append(("\n%s:\n" % title, None))
614 for shortopt, longopt, default, desc in options:
614 for shortopt, longopt, default, desc in options:
615 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
615 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
616 longopt and " --%s" % longopt),
616 longopt and " --%s" % longopt),
617 "%s%s" % (desc,
617 "%s%s" % (desc,
618 default
618 default
619 and _(" (default: %s)") % default
619 and _(" (default: %s)") % default
620 or "")))
620 or "")))
621
621
622 if opt_output:
622 if opt_output:
623 opts_len = max([len(line[0]) for line in opt_output if line[1]])
623 opts_len = max([len(line[0]) for line in opt_output if line[1]])
624 for first, second in opt_output:
624 for first, second in opt_output:
625 if second:
625 if second:
626 ui.write(" %-*s %s\n" % (opts_len, first, second))
626 ui.write(" %-*s %s\n" % (opts_len, first, second))
627 else:
627 else:
628 ui.write("%s\n" % first)
628 ui.write("%s\n" % first)
629
629
630 # Commands start here, listed alphabetically
630 # Commands start here, listed alphabetically
631
631
632 def add(ui, repo, *pats, **opts):
632 def add(ui, repo, *pats, **opts):
633 """add the specified files on the next commit
633 """add the specified files on the next commit
634
634
635 Schedule files to be version controlled and added to the repository.
635 Schedule files to be version controlled and added to the repository.
636
636
637 The files will be added to the repository at the next commit.
637 The files will be added to the repository at the next commit.
638
638
639 If no names are given, add all files in the repository.
639 If no names are given, add all files in the repository.
640 """
640 """
641
641
642 names = []
642 names = []
643 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
643 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
644 if exact:
644 if exact:
645 if ui.verbose:
645 if ui.verbose:
646 ui.status(_('adding %s\n') % rel)
646 ui.status(_('adding %s\n') % rel)
647 names.append(abs)
647 names.append(abs)
648 elif repo.dirstate.state(abs) == '?':
648 elif repo.dirstate.state(abs) == '?':
649 ui.status(_('adding %s\n') % rel)
649 ui.status(_('adding %s\n') % rel)
650 names.append(abs)
650 names.append(abs)
651 if not opts.get('dry_run'):
651 if not opts.get('dry_run'):
652 repo.add(names)
652 repo.add(names)
653
653
654 def addremove(ui, repo, *pats, **opts):
654 def addremove(ui, repo, *pats, **opts):
655 """add all new files, delete all missing files (DEPRECATED)
655 """add all new files, delete all missing files (DEPRECATED)
656
656
657 (DEPRECATED)
657 (DEPRECATED)
658 Add all new files and remove all missing files from the repository.
658 Add all new files and remove all missing files from the repository.
659
659
660 New files are ignored if they match any of the patterns in .hgignore. As
660 New files are ignored if they match any of the patterns in .hgignore. As
661 with add, these changes take effect at the next commit.
661 with add, these changes take effect at the next commit.
662
662
663 This command is now deprecated and will be removed in a future
663 This command is now deprecated and will be removed in a future
664 release. Please use add and remove --after instead.
664 release. Please use add and remove --after instead.
665 """
665 """
666 ui.warn(_('(the addremove command is deprecated; use add and remove '
666 ui.warn(_('(the addremove command is deprecated; use add and remove '
667 '--after instead)\n'))
667 '--after instead)\n'))
668 return cmdutil.addremove(repo, pats, opts)
668 return cmdutil.addremove(repo, pats, opts)
669
669
670 def annotate(ui, repo, *pats, **opts):
670 def annotate(ui, repo, *pats, **opts):
671 """show changeset information per file line
671 """show changeset information per file line
672
672
673 List changes in files, showing the revision id responsible for each line
673 List changes in files, showing the revision id responsible for each line
674
674
675 This command is useful to discover who did a change or when a change took
675 This command is useful to discover who did a change or when a change took
676 place.
676 place.
677
677
678 Without the -a option, annotate will avoid processing files it
678 Without the -a option, annotate will avoid processing files it
679 detects as binary. With -a, annotate will generate an annotation
679 detects as binary. With -a, annotate will generate an annotation
680 anyway, probably with undesirable results.
680 anyway, probably with undesirable results.
681 """
681 """
682 def getnode(rev):
682 def getnode(rev):
683 return short(repo.changelog.node(rev))
683 return short(repo.changelog.node(rev))
684
684
685 ucache = {}
685 ucache = {}
686 def getname(rev):
686 def getname(rev):
687 try:
687 try:
688 return ucache[rev]
688 return ucache[rev]
689 except:
689 except:
690 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
690 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
691 ucache[rev] = u
691 ucache[rev] = u
692 return u
692 return u
693
693
694 dcache = {}
694 dcache = {}
695 def getdate(rev):
695 def getdate(rev):
696 datestr = dcache.get(rev)
696 datestr = dcache.get(rev)
697 if datestr is None:
697 if datestr is None:
698 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
698 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
699 return datestr
699 return datestr
700
700
701 if not pats:
701 if not pats:
702 raise util.Abort(_('at least one file name or pattern required'))
702 raise util.Abort(_('at least one file name or pattern required'))
703
703
704 opmap = [['user', getname], ['number', str], ['changeset', getnode],
704 opmap = [['user', getname], ['number', str], ['changeset', getnode],
705 ['date', getdate]]
705 ['date', getdate]]
706 if not opts['user'] and not opts['changeset'] and not opts['date']:
706 if not opts['user'] and not opts['changeset'] and not opts['date']:
707 opts['number'] = 1
707 opts['number'] = 1
708
708
709 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
709 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
710
710
711 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
711 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
712 node=ctx.node()):
712 node=ctx.node()):
713 fctx = ctx.filectx(abs)
713 fctx = ctx.filectx(abs)
714 if not opts['text'] and util.binary(fctx.data()):
714 if not opts['text'] and util.binary(fctx.data()):
715 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
715 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
716 continue
716 continue
717
717
718 lines = fctx.annotate()
718 lines = fctx.annotate()
719 pieces = []
719 pieces = []
720
720
721 for o, f in opmap:
721 for o, f in opmap:
722 if opts[o]:
722 if opts[o]:
723 l = [f(n) for n, dummy in lines]
723 l = [f(n) for n, dummy in lines]
724 if l:
724 if l:
725 m = max(map(len, l))
725 m = max(map(len, l))
726 pieces.append(["%*s" % (m, x) for x in l])
726 pieces.append(["%*s" % (m, x) for x in l])
727
727
728 if pieces:
728 if pieces:
729 for p, l in zip(zip(*pieces), lines):
729 for p, l in zip(zip(*pieces), lines):
730 ui.write("%s: %s" % (" ".join(p), l[1]))
730 ui.write("%s: %s" % (" ".join(p), l[1]))
731
731
732 def archive(ui, repo, dest, **opts):
732 def archive(ui, repo, dest, **opts):
733 '''create unversioned archive of a repository revision
733 '''create unversioned archive of a repository revision
734
734
735 By default, the revision used is the parent of the working
735 By default, the revision used is the parent of the working
736 directory; use "-r" to specify a different revision.
736 directory; use "-r" to specify a different revision.
737
737
738 To specify the type of archive to create, use "-t". Valid
738 To specify the type of archive to create, use "-t". Valid
739 types are:
739 types are:
740
740
741 "files" (default): a directory full of files
741 "files" (default): a directory full of files
742 "tar": tar archive, uncompressed
742 "tar": tar archive, uncompressed
743 "tbz2": tar archive, compressed using bzip2
743 "tbz2": tar archive, compressed using bzip2
744 "tgz": tar archive, compressed using gzip
744 "tgz": tar archive, compressed using gzip
745 "uzip": zip archive, uncompressed
745 "uzip": zip archive, uncompressed
746 "zip": zip archive, compressed using deflate
746 "zip": zip archive, compressed using deflate
747
747
748 The exact name of the destination archive or directory is given
748 The exact name of the destination archive or directory is given
749 using a format string; see "hg help export" for details.
749 using a format string; see "hg help export" for details.
750
750
751 Each member added to an archive file has a directory prefix
751 Each member added to an archive file has a directory prefix
752 prepended. Use "-p" to specify a format string for the prefix.
752 prepended. Use "-p" to specify a format string for the prefix.
753 The default is the basename of the archive, with suffixes removed.
753 The default is the basename of the archive, with suffixes removed.
754 '''
754 '''
755
755
756 if opts['rev']:
756 if opts['rev']:
757 node = repo.lookup(opts['rev'])
757 node = repo.lookup(opts['rev'])
758 else:
758 else:
759 node, p2 = repo.dirstate.parents()
759 node, p2 = repo.dirstate.parents()
760 if p2 != nullid:
760 if p2 != nullid:
761 raise util.Abort(_('uncommitted merge - please provide a '
761 raise util.Abort(_('uncommitted merge - please provide a '
762 'specific revision'))
762 'specific revision'))
763
763
764 dest = cmdutil.make_filename(repo, dest, node)
764 dest = cmdutil.make_filename(repo, dest, node)
765 if os.path.realpath(dest) == repo.root:
765 if os.path.realpath(dest) == repo.root:
766 raise util.Abort(_('repository root cannot be destination'))
766 raise util.Abort(_('repository root cannot be destination'))
767 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
767 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
768 kind = opts.get('type') or 'files'
768 kind = opts.get('type') or 'files'
769 prefix = opts['prefix']
769 prefix = opts['prefix']
770 if dest == '-':
770 if dest == '-':
771 if kind == 'files':
771 if kind == 'files':
772 raise util.Abort(_('cannot archive plain files to stdout'))
772 raise util.Abort(_('cannot archive plain files to stdout'))
773 dest = sys.stdout
773 dest = sys.stdout
774 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
774 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
775 prefix = cmdutil.make_filename(repo, prefix, node)
775 prefix = cmdutil.make_filename(repo, prefix, node)
776 archival.archive(repo, dest, node, kind, not opts['no_decode'],
776 archival.archive(repo, dest, node, kind, not opts['no_decode'],
777 matchfn, prefix)
777 matchfn, prefix)
778
778
779 def backout(ui, repo, rev, **opts):
779 def backout(ui, repo, rev, **opts):
780 '''reverse effect of earlier changeset
780 '''reverse effect of earlier changeset
781
781
782 Commit the backed out changes as a new changeset. The new
782 Commit the backed out changes as a new changeset. The new
783 changeset is a child of the backed out changeset.
783 changeset is a child of the backed out changeset.
784
784
785 If you back out a changeset other than the tip, a new head is
785 If you back out a changeset other than the tip, a new head is
786 created. This head is the parent of the working directory. If
786 created. This head is the parent of the working directory. If
787 you back out an old changeset, your working directory will appear
787 you back out an old changeset, your working directory will appear
788 old after the backout. You should merge the backout changeset
788 old after the backout. You should merge the backout changeset
789 with another head.
789 with another head.
790
790
791 The --merge option remembers the parent of the working directory
791 The --merge option remembers the parent of the working directory
792 before starting the backout, then merges the new head with that
792 before starting the backout, then merges the new head with that
793 changeset afterwards. This saves you from doing the merge by
793 changeset afterwards. This saves you from doing the merge by
794 hand. The result of this merge is not committed, as for a normal
794 hand. The result of this merge is not committed, as for a normal
795 merge.'''
795 merge.'''
796
796
797 bail_if_changed(repo)
797 bail_if_changed(repo)
798 op1, op2 = repo.dirstate.parents()
798 op1, op2 = repo.dirstate.parents()
799 if op2 != nullid:
799 if op2 != nullid:
800 raise util.Abort(_('outstanding uncommitted merge'))
800 raise util.Abort(_('outstanding uncommitted merge'))
801 node = repo.lookup(rev)
801 node = repo.lookup(rev)
802 p1, p2 = repo.changelog.parents(node)
802 p1, p2 = repo.changelog.parents(node)
803 if p1 == nullid:
803 if p1 == nullid:
804 raise util.Abort(_('cannot back out a change with no parents'))
804 raise util.Abort(_('cannot back out a change with no parents'))
805 if p2 != nullid:
805 if p2 != nullid:
806 if not opts['parent']:
806 if not opts['parent']:
807 raise util.Abort(_('cannot back out a merge changeset without '
807 raise util.Abort(_('cannot back out a merge changeset without '
808 '--parent'))
808 '--parent'))
809 p = repo.lookup(opts['parent'])
809 p = repo.lookup(opts['parent'])
810 if p not in (p1, p2):
810 if p not in (p1, p2):
811 raise util.Abort(_('%s is not a parent of %s' %
811 raise util.Abort(_('%s is not a parent of %s' %
812 (short(p), short(node))))
812 (short(p), short(node))))
813 parent = p
813 parent = p
814 else:
814 else:
815 if opts['parent']:
815 if opts['parent']:
816 raise util.Abort(_('cannot use --parent on non-merge changeset'))
816 raise util.Abort(_('cannot use --parent on non-merge changeset'))
817 parent = p1
817 parent = p1
818 hg.clean(repo, node, show_stats=False)
818 hg.clean(repo, node, show_stats=False)
819 revert_opts = opts.copy()
819 revert_opts = opts.copy()
820 revert_opts['rev'] = hex(parent)
820 revert_opts['rev'] = hex(parent)
821 revert(ui, repo, **revert_opts)
821 revert(ui, repo, **revert_opts)
822 commit_opts = opts.copy()
822 commit_opts = opts.copy()
823 commit_opts['addremove'] = False
823 commit_opts['addremove'] = False
824 if not commit_opts['message'] and not commit_opts['logfile']:
824 if not commit_opts['message'] and not commit_opts['logfile']:
825 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
825 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
826 commit_opts['force_editor'] = True
826 commit_opts['force_editor'] = True
827 commit(ui, repo, **commit_opts)
827 commit(ui, repo, **commit_opts)
828 def nice(node):
828 def nice(node):
829 return '%d:%s' % (repo.changelog.rev(node), short(node))
829 return '%d:%s' % (repo.changelog.rev(node), short(node))
830 ui.status(_('changeset %s backs out changeset %s\n') %
830 ui.status(_('changeset %s backs out changeset %s\n') %
831 (nice(repo.changelog.tip()), nice(node)))
831 (nice(repo.changelog.tip()), nice(node)))
832 if op1 != node:
832 if op1 != node:
833 if opts['merge']:
833 if opts['merge']:
834 ui.status(_('merging with changeset %s\n') % nice(op1))
834 ui.status(_('merging with changeset %s\n') % nice(op1))
835 n = _lookup(repo, hex(op1))
835 n = _lookup(repo, hex(op1))
836 hg.merge(repo, n)
836 hg.merge(repo, n)
837 else:
837 else:
838 ui.status(_('the backout changeset is a new head - '
838 ui.status(_('the backout changeset is a new head - '
839 'do not forget to merge\n'))
839 'do not forget to merge\n'))
840 ui.status(_('(use "backout --merge" '
840 ui.status(_('(use "backout --merge" '
841 'if you want to auto-merge)\n'))
841 'if you want to auto-merge)\n'))
842
842
843 def bundle(ui, repo, fname, dest=None, **opts):
843 def bundle(ui, repo, fname, dest=None, **opts):
844 """create a changegroup file
844 """create a changegroup file
845
845
846 Generate a compressed changegroup file collecting all changesets
846 Generate a compressed changegroup file collecting all changesets
847 not found in the other repository.
847 not found in the other repository.
848
848
849 This file can then be transferred using conventional means and
849 This file can then be transferred using conventional means and
850 applied to another repository with the unbundle command. This is
850 applied to another repository with the unbundle command. This is
851 useful when native push and pull are not available or when
851 useful when native push and pull are not available or when
852 exporting an entire repository is undesirable. The standard file
852 exporting an entire repository is undesirable. The standard file
853 extension is ".hg".
853 extension is ".hg".
854
854
855 Unlike import/export, this exactly preserves all changeset
855 Unlike import/export, this exactly preserves all changeset
856 contents including permissions, rename data, and revision history.
856 contents including permissions, rename data, and revision history.
857 """
857 """
858 dest = ui.expandpath(dest or 'default-push', dest or 'default')
858 dest = ui.expandpath(dest or 'default-push', dest or 'default')
859 other = hg.repository(ui, dest)
859 other = hg.repository(ui, dest)
860 o = repo.findoutgoing(other, force=opts['force'])
860 o = repo.findoutgoing(other, force=opts['force'])
861 cg = repo.changegroup(o, 'bundle')
861 cg = repo.changegroup(o, 'bundle')
862 write_bundle(cg, fname)
862 write_bundle(cg, fname)
863
863
864 def cat(ui, repo, file1, *pats, **opts):
864 def cat(ui, repo, file1, *pats, **opts):
865 """output the latest or given revisions of files
865 """output the latest or given revisions of files
866
866
867 Print the specified files as they were at the given revision.
867 Print the specified files as they were at the given revision.
868 If no revision is given then the tip is used.
868 If no revision is given then the tip is used.
869
869
870 Output may be to a file, in which case the name of the file is
870 Output may be to a file, in which case the name of the file is
871 given using a format string. The formatting rules are the same as
871 given using a format string. The formatting rules are the same as
872 for the export command, with the following additions:
872 for the export command, with the following additions:
873
873
874 %s basename of file being printed
874 %s basename of file being printed
875 %d dirname of file being printed, or '.' if in repo root
875 %d dirname of file being printed, or '.' if in repo root
876 %p root-relative path name of file being printed
876 %p root-relative path name of file being printed
877 """
877 """
878 ctx = repo.changectx(opts['rev'] or "-1")
878 ctx = repo.changectx(opts['rev'] or "-1")
879 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
879 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
880 ctx.node()):
880 ctx.node()):
881 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
881 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
882 fp.write(ctx.filectx(abs).data())
882 fp.write(ctx.filectx(abs).data())
883
883
884 def clone(ui, source, dest=None, **opts):
884 def clone(ui, source, dest=None, **opts):
885 """make a copy of an existing repository
885 """make a copy of an existing repository
886
886
887 Create a copy of an existing repository in a new directory.
887 Create a copy of an existing repository in a new directory.
888
888
889 If no destination directory name is specified, it defaults to the
889 If no destination directory name is specified, it defaults to the
890 basename of the source.
890 basename of the source.
891
891
892 The location of the source is added to the new repository's
892 The location of the source is added to the new repository's
893 .hg/hgrc file, as the default to be used for future pulls.
893 .hg/hgrc file, as the default to be used for future pulls.
894
894
895 For efficiency, hardlinks are used for cloning whenever the source
895 For efficiency, hardlinks are used for cloning whenever the source
896 and destination are on the same filesystem (note this applies only
896 and destination are on the same filesystem (note this applies only
897 to the repository data, not to the checked out files). Some
897 to the repository data, not to the checked out files). Some
898 filesystems, such as AFS, implement hardlinking incorrectly, but
898 filesystems, such as AFS, implement hardlinking incorrectly, but
899 do not report errors. In these cases, use the --pull option to
899 do not report errors. In these cases, use the --pull option to
900 avoid hardlinking.
900 avoid hardlinking.
901
901
902 You can safely clone repositories and checked out files using full
902 You can safely clone repositories and checked out files using full
903 hardlinks with
903 hardlinks with
904
904
905 $ cp -al REPO REPOCLONE
905 $ cp -al REPO REPOCLONE
906
906
907 which is the fastest way to clone. However, the operation is not
907 which is the fastest way to clone. However, the operation is not
908 atomic (making sure REPO is not modified during the operation is
908 atomic (making sure REPO is not modified during the operation is
909 up to you) and you have to make sure your editor breaks hardlinks
909 up to you) and you have to make sure your editor breaks hardlinks
910 (Emacs and most Linux Kernel tools do so).
910 (Emacs and most Linux Kernel tools do so).
911
911
912 If you use the -r option to clone up to a specific revision, no
912 If you use the -r option to clone up to a specific revision, no
913 subsequent revisions will be present in the cloned repository.
913 subsequent revisions will be present in the cloned repository.
914 This option implies --pull, even on local repositories.
914 This option implies --pull, even on local repositories.
915
915
916 See pull for valid source format details.
916 See pull for valid source format details.
917
917
918 It is possible to specify an ssh:// URL as the destination, but no
918 It is possible to specify an ssh:// URL as the destination, but no
919 .hg/hgrc will be created on the remote side. Look at the help text
919 .hg/hgrc will be created on the remote side. Look at the help text
920 for the pull command for important details about ssh:// URLs.
920 for the pull command for important details about ssh:// URLs.
921 """
921 """
922 setremoteconfig(ui, opts)
922 setremoteconfig(ui, opts)
923 hg.clone(ui, ui.expandpath(source), dest,
923 hg.clone(ui, ui.expandpath(source), dest,
924 pull=opts['pull'],
924 pull=opts['pull'],
925 stream=opts['uncompressed'],
925 stream=opts['uncompressed'],
926 rev=opts['rev'],
926 rev=opts['rev'],
927 update=not opts['noupdate'])
927 update=not opts['noupdate'])
928
928
929 def commit(ui, repo, *pats, **opts):
929 def commit(ui, repo, *pats, **opts):
930 """commit the specified files or all outstanding changes
930 """commit the specified files or all outstanding changes
931
931
932 Commit changes to the given files into the repository.
932 Commit changes to the given files into the repository.
933
933
934 If a list of files is omitted, all changes reported by "hg status"
934 If a list of files is omitted, all changes reported by "hg status"
935 will be committed.
935 will be committed.
936
936
937 If no commit message is specified, the editor configured in your hgrc
937 If no commit message is specified, the editor configured in your hgrc
938 or in the EDITOR environment variable is started to enter a message.
938 or in the EDITOR environment variable is started to enter a message.
939 """
939 """
940 message = logmessage(opts)
940 message = logmessage(opts)
941
941
942 if opts['addremove']:
942 if opts['addremove']:
943 cmdutil.addremove(repo, pats, opts)
943 cmdutil.addremove(repo, pats, opts)
944 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
944 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
945 if pats:
945 if pats:
946 modified, added, removed = repo.status(files=fns, match=match)[:3]
946 modified, added, removed = repo.status(files=fns, match=match)[:3]
947 files = modified + added + removed
947 files = modified + added + removed
948 else:
948 else:
949 files = []
949 files = []
950 try:
950 try:
951 repo.commit(files, message, opts['user'], opts['date'], match,
951 repo.commit(files, message, opts['user'], opts['date'], match,
952 force_editor=opts.get('force_editor'))
952 force_editor=opts.get('force_editor'))
953 except ValueError, inst:
953 except ValueError, inst:
954 raise util.Abort(str(inst))
954 raise util.Abort(str(inst))
955
955
956 def docopy(ui, repo, pats, opts, wlock):
956 def docopy(ui, repo, pats, opts, wlock):
957 # called with the repo lock held
957 # called with the repo lock held
958 cwd = repo.getcwd()
958 cwd = repo.getcwd()
959 errors = 0
959 errors = 0
960 copied = []
960 copied = []
961 targets = {}
961 targets = {}
962
962
963 def okaytocopy(abs, rel, exact):
963 def okaytocopy(abs, rel, exact):
964 reasons = {'?': _('is not managed'),
964 reasons = {'?': _('is not managed'),
965 'a': _('has been marked for add'),
965 'a': _('has been marked for add'),
966 'r': _('has been marked for remove')}
966 'r': _('has been marked for remove')}
967 state = repo.dirstate.state(abs)
967 state = repo.dirstate.state(abs)
968 reason = reasons.get(state)
968 reason = reasons.get(state)
969 if reason:
969 if reason:
970 if state == 'a':
970 if state == 'a':
971 origsrc = repo.dirstate.copied(abs)
971 origsrc = repo.dirstate.copied(abs)
972 if origsrc is not None:
972 if origsrc is not None:
973 return origsrc
973 return origsrc
974 if exact:
974 if exact:
975 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
975 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
976 else:
976 else:
977 return abs
977 return abs
978
978
979 def copy(origsrc, abssrc, relsrc, target, exact):
979 def copy(origsrc, abssrc, relsrc, target, exact):
980 abstarget = util.canonpath(repo.root, cwd, target)
980 abstarget = util.canonpath(repo.root, cwd, target)
981 reltarget = util.pathto(cwd, abstarget)
981 reltarget = util.pathto(cwd, abstarget)
982 prevsrc = targets.get(abstarget)
982 prevsrc = targets.get(abstarget)
983 if prevsrc is not None:
983 if prevsrc is not None:
984 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
984 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
985 (reltarget, abssrc, prevsrc))
985 (reltarget, abssrc, prevsrc))
986 return
986 return
987 if (not opts['after'] and os.path.exists(reltarget) or
987 if (not opts['after'] and os.path.exists(reltarget) or
988 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
988 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
989 if not opts['force']:
989 if not opts['force']:
990 ui.warn(_('%s: not overwriting - file exists\n') %
990 ui.warn(_('%s: not overwriting - file exists\n') %
991 reltarget)
991 reltarget)
992 return
992 return
993 if not opts['after'] and not opts.get('dry_run'):
993 if not opts['after'] and not opts.get('dry_run'):
994 os.unlink(reltarget)
994 os.unlink(reltarget)
995 if opts['after']:
995 if opts['after']:
996 if not os.path.exists(reltarget):
996 if not os.path.exists(reltarget):
997 return
997 return
998 else:
998 else:
999 targetdir = os.path.dirname(reltarget) or '.'
999 targetdir = os.path.dirname(reltarget) or '.'
1000 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1000 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1001 os.makedirs(targetdir)
1001 os.makedirs(targetdir)
1002 try:
1002 try:
1003 restore = repo.dirstate.state(abstarget) == 'r'
1003 restore = repo.dirstate.state(abstarget) == 'r'
1004 if restore and not opts.get('dry_run'):
1004 if restore and not opts.get('dry_run'):
1005 repo.undelete([abstarget], wlock)
1005 repo.undelete([abstarget], wlock)
1006 try:
1006 try:
1007 if not opts.get('dry_run'):
1007 if not opts.get('dry_run'):
1008 shutil.copyfile(relsrc, reltarget)
1008 shutil.copyfile(relsrc, reltarget)
1009 shutil.copymode(relsrc, reltarget)
1009 shutil.copymode(relsrc, reltarget)
1010 restore = False
1010 restore = False
1011 finally:
1011 finally:
1012 if restore:
1012 if restore:
1013 repo.remove([abstarget], wlock)
1013 repo.remove([abstarget], wlock)
1014 except shutil.Error, inst:
1014 except shutil.Error, inst:
1015 raise util.Abort(str(inst))
1015 raise util.Abort(str(inst))
1016 except IOError, inst:
1016 except IOError, inst:
1017 if inst.errno == errno.ENOENT:
1017 if inst.errno == errno.ENOENT:
1018 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1018 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1019 else:
1019 else:
1020 ui.warn(_('%s: cannot copy - %s\n') %
1020 ui.warn(_('%s: cannot copy - %s\n') %
1021 (relsrc, inst.strerror))
1021 (relsrc, inst.strerror))
1022 errors += 1
1022 errors += 1
1023 return
1023 return
1024 if ui.verbose or not exact:
1024 if ui.verbose or not exact:
1025 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1025 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1026 targets[abstarget] = abssrc
1026 targets[abstarget] = abssrc
1027 if abstarget != origsrc and not opts.get('dry_run'):
1027 if abstarget != origsrc and not opts.get('dry_run'):
1028 repo.copy(origsrc, abstarget, wlock)
1028 repo.copy(origsrc, abstarget, wlock)
1029 copied.append((abssrc, relsrc, exact))
1029 copied.append((abssrc, relsrc, exact))
1030
1030
1031 def targetpathfn(pat, dest, srcs):
1031 def targetpathfn(pat, dest, srcs):
1032 if os.path.isdir(pat):
1032 if os.path.isdir(pat):
1033 abspfx = util.canonpath(repo.root, cwd, pat)
1033 abspfx = util.canonpath(repo.root, cwd, pat)
1034 if destdirexists:
1034 if destdirexists:
1035 striplen = len(os.path.split(abspfx)[0])
1035 striplen = len(os.path.split(abspfx)[0])
1036 else:
1036 else:
1037 striplen = len(abspfx)
1037 striplen = len(abspfx)
1038 if striplen:
1038 if striplen:
1039 striplen += len(os.sep)
1039 striplen += len(os.sep)
1040 res = lambda p: os.path.join(dest, p[striplen:])
1040 res = lambda p: os.path.join(dest, p[striplen:])
1041 elif destdirexists:
1041 elif destdirexists:
1042 res = lambda p: os.path.join(dest, os.path.basename(p))
1042 res = lambda p: os.path.join(dest, os.path.basename(p))
1043 else:
1043 else:
1044 res = lambda p: dest
1044 res = lambda p: dest
1045 return res
1045 return res
1046
1046
1047 def targetpathafterfn(pat, dest, srcs):
1047 def targetpathafterfn(pat, dest, srcs):
1048 if util.patkind(pat, None)[0]:
1048 if util.patkind(pat, None)[0]:
1049 # a mercurial pattern
1049 # a mercurial pattern
1050 res = lambda p: os.path.join(dest, os.path.basename(p))
1050 res = lambda p: os.path.join(dest, os.path.basename(p))
1051 else:
1051 else:
1052 abspfx = util.canonpath(repo.root, cwd, pat)
1052 abspfx = util.canonpath(repo.root, cwd, pat)
1053 if len(abspfx) < len(srcs[0][0]):
1053 if len(abspfx) < len(srcs[0][0]):
1054 # A directory. Either the target path contains the last
1054 # A directory. Either the target path contains the last
1055 # component of the source path or it does not.
1055 # component of the source path or it does not.
1056 def evalpath(striplen):
1056 def evalpath(striplen):
1057 score = 0
1057 score = 0
1058 for s in srcs:
1058 for s in srcs:
1059 t = os.path.join(dest, s[0][striplen:])
1059 t = os.path.join(dest, s[0][striplen:])
1060 if os.path.exists(t):
1060 if os.path.exists(t):
1061 score += 1
1061 score += 1
1062 return score
1062 return score
1063
1063
1064 striplen = len(abspfx)
1064 striplen = len(abspfx)
1065 if striplen:
1065 if striplen:
1066 striplen += len(os.sep)
1066 striplen += len(os.sep)
1067 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1067 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1068 score = evalpath(striplen)
1068 score = evalpath(striplen)
1069 striplen1 = len(os.path.split(abspfx)[0])
1069 striplen1 = len(os.path.split(abspfx)[0])
1070 if striplen1:
1070 if striplen1:
1071 striplen1 += len(os.sep)
1071 striplen1 += len(os.sep)
1072 if evalpath(striplen1) > score:
1072 if evalpath(striplen1) > score:
1073 striplen = striplen1
1073 striplen = striplen1
1074 res = lambda p: os.path.join(dest, p[striplen:])
1074 res = lambda p: os.path.join(dest, p[striplen:])
1075 else:
1075 else:
1076 # a file
1076 # a file
1077 if destdirexists:
1077 if destdirexists:
1078 res = lambda p: os.path.join(dest, os.path.basename(p))
1078 res = lambda p: os.path.join(dest, os.path.basename(p))
1079 else:
1079 else:
1080 res = lambda p: dest
1080 res = lambda p: dest
1081 return res
1081 return res
1082
1082
1083
1083
1084 pats = list(pats)
1084 pats = list(pats)
1085 if not pats:
1085 if not pats:
1086 raise util.Abort(_('no source or destination specified'))
1086 raise util.Abort(_('no source or destination specified'))
1087 if len(pats) == 1:
1087 if len(pats) == 1:
1088 raise util.Abort(_('no destination specified'))
1088 raise util.Abort(_('no destination specified'))
1089 dest = pats.pop()
1089 dest = pats.pop()
1090 destdirexists = os.path.isdir(dest)
1090 destdirexists = os.path.isdir(dest)
1091 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1091 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1092 raise util.Abort(_('with multiple sources, destination must be an '
1092 raise util.Abort(_('with multiple sources, destination must be an '
1093 'existing directory'))
1093 'existing directory'))
1094 if opts['after']:
1094 if opts['after']:
1095 tfn = targetpathafterfn
1095 tfn = targetpathafterfn
1096 else:
1096 else:
1097 tfn = targetpathfn
1097 tfn = targetpathfn
1098 copylist = []
1098 copylist = []
1099 for pat in pats:
1099 for pat in pats:
1100 srcs = []
1100 srcs = []
1101 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1101 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1102 origsrc = okaytocopy(abssrc, relsrc, exact)
1102 origsrc = okaytocopy(abssrc, relsrc, exact)
1103 if origsrc:
1103 if origsrc:
1104 srcs.append((origsrc, abssrc, relsrc, exact))
1104 srcs.append((origsrc, abssrc, relsrc, exact))
1105 if not srcs:
1105 if not srcs:
1106 continue
1106 continue
1107 copylist.append((tfn(pat, dest, srcs), srcs))
1107 copylist.append((tfn(pat, dest, srcs), srcs))
1108 if not copylist:
1108 if not copylist:
1109 raise util.Abort(_('no files to copy'))
1109 raise util.Abort(_('no files to copy'))
1110
1110
1111 for targetpath, srcs in copylist:
1111 for targetpath, srcs in copylist:
1112 for origsrc, abssrc, relsrc, exact in srcs:
1112 for origsrc, abssrc, relsrc, exact in srcs:
1113 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1113 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1114
1114
1115 if errors:
1115 if errors:
1116 ui.warn(_('(consider using --after)\n'))
1116 ui.warn(_('(consider using --after)\n'))
1117 return errors, copied
1117 return errors, copied
1118
1118
1119 def copy(ui, repo, *pats, **opts):
1119 def copy(ui, repo, *pats, **opts):
1120 """mark files as copied for the next commit
1120 """mark files as copied for the next commit
1121
1121
1122 Mark dest as having copies of source files. If dest is a
1122 Mark dest as having copies of source files. If dest is a
1123 directory, copies are put in that directory. If dest is a file,
1123 directory, copies are put in that directory. If dest is a file,
1124 there can only be one source.
1124 there can only be one source.
1125
1125
1126 By default, this command copies the contents of files as they
1126 By default, this command copies the contents of files as they
1127 stand in the working directory. If invoked with --after, the
1127 stand in the working directory. If invoked with --after, the
1128 operation is recorded, but no copying is performed.
1128 operation is recorded, but no copying is performed.
1129
1129
1130 This command takes effect in the next commit.
1130 This command takes effect in the next commit.
1131
1131
1132 NOTE: This command should be treated as experimental. While it
1132 NOTE: This command should be treated as experimental. While it
1133 should properly record copied files, this information is not yet
1133 should properly record copied files, this information is not yet
1134 fully used by merge, nor fully reported by log.
1134 fully used by merge, nor fully reported by log.
1135 """
1135 """
1136 wlock = repo.wlock(0)
1136 wlock = repo.wlock(0)
1137 errs, copied = docopy(ui, repo, pats, opts, wlock)
1137 errs, copied = docopy(ui, repo, pats, opts, wlock)
1138 return errs
1138 return errs
1139
1139
1140 def debugancestor(ui, index, rev1, rev2):
1140 def debugancestor(ui, index, rev1, rev2):
1141 """find the ancestor revision of two revisions in a given index"""
1141 """find the ancestor revision of two revisions in a given index"""
1142 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1142 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1143 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1143 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1144 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1144 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1145
1145
1146 def debugcomplete(ui, cmd='', **opts):
1146 def debugcomplete(ui, cmd='', **opts):
1147 """returns the completion list associated with the given command"""
1147 """returns the completion list associated with the given command"""
1148
1148
1149 if opts['options']:
1149 if opts['options']:
1150 options = []
1150 options = []
1151 otables = [globalopts]
1151 otables = [globalopts]
1152 if cmd:
1152 if cmd:
1153 aliases, entry = findcmd(cmd)
1153 aliases, entry = findcmd(cmd)
1154 otables.append(entry[1])
1154 otables.append(entry[1])
1155 for t in otables:
1155 for t in otables:
1156 for o in t:
1156 for o in t:
1157 if o[0]:
1157 if o[0]:
1158 options.append('-%s' % o[0])
1158 options.append('-%s' % o[0])
1159 options.append('--%s' % o[1])
1159 options.append('--%s' % o[1])
1160 ui.write("%s\n" % "\n".join(options))
1160 ui.write("%s\n" % "\n".join(options))
1161 return
1161 return
1162
1162
1163 clist = findpossible(cmd).keys()
1163 clist = findpossible(cmd).keys()
1164 clist.sort()
1164 clist.sort()
1165 ui.write("%s\n" % "\n".join(clist))
1165 ui.write("%s\n" % "\n".join(clist))
1166
1166
1167 def debugrebuildstate(ui, repo, rev=None):
1167 def debugrebuildstate(ui, repo, rev=None):
1168 """rebuild the dirstate as it would look like for the given revision"""
1168 """rebuild the dirstate as it would look like for the given revision"""
1169 if not rev:
1169 if not rev:
1170 rev = repo.changelog.tip()
1170 rev = repo.changelog.tip()
1171 else:
1171 else:
1172 rev = repo.lookup(rev)
1172 rev = repo.lookup(rev)
1173 change = repo.changelog.read(rev)
1173 change = repo.changelog.read(rev)
1174 n = change[0]
1174 n = change[0]
1175 files = repo.manifest.read(n)
1175 files = repo.manifest.read(n)
1176 wlock = repo.wlock()
1176 wlock = repo.wlock()
1177 repo.dirstate.rebuild(rev, files)
1177 repo.dirstate.rebuild(rev, files)
1178
1178
1179 def debugcheckstate(ui, repo):
1179 def debugcheckstate(ui, repo):
1180 """validate the correctness of the current dirstate"""
1180 """validate the correctness of the current dirstate"""
1181 parent1, parent2 = repo.dirstate.parents()
1181 parent1, parent2 = repo.dirstate.parents()
1182 repo.dirstate.read()
1182 repo.dirstate.read()
1183 dc = repo.dirstate.map
1183 dc = repo.dirstate.map
1184 keys = dc.keys()
1184 keys = dc.keys()
1185 keys.sort()
1185 keys.sort()
1186 m1n = repo.changelog.read(parent1)[0]
1186 m1n = repo.changelog.read(parent1)[0]
1187 m2n = repo.changelog.read(parent2)[0]
1187 m2n = repo.changelog.read(parent2)[0]
1188 m1 = repo.manifest.read(m1n)
1188 m1 = repo.manifest.read(m1n)
1189 m2 = repo.manifest.read(m2n)
1189 m2 = repo.manifest.read(m2n)
1190 errors = 0
1190 errors = 0
1191 for f in dc:
1191 for f in dc:
1192 state = repo.dirstate.state(f)
1192 state = repo.dirstate.state(f)
1193 if state in "nr" and f not in m1:
1193 if state in "nr" and f not in m1:
1194 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1194 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1195 errors += 1
1195 errors += 1
1196 if state in "a" and f in m1:
1196 if state in "a" and f in m1:
1197 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1197 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1198 errors += 1
1198 errors += 1
1199 if state in "m" and f not in m1 and f not in m2:
1199 if state in "m" and f not in m1 and f not in m2:
1200 ui.warn(_("%s in state %s, but not in either manifest\n") %
1200 ui.warn(_("%s in state %s, but not in either manifest\n") %
1201 (f, state))
1201 (f, state))
1202 errors += 1
1202 errors += 1
1203 for f in m1:
1203 for f in m1:
1204 state = repo.dirstate.state(f)
1204 state = repo.dirstate.state(f)
1205 if state not in "nrm":
1205 if state not in "nrm":
1206 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1206 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1207 errors += 1
1207 errors += 1
1208 if errors:
1208 if errors:
1209 error = _(".hg/dirstate inconsistent with current parent's manifest")
1209 error = _(".hg/dirstate inconsistent with current parent's manifest")
1210 raise util.Abort(error)
1210 raise util.Abort(error)
1211
1211
1212 def debugconfig(ui, repo, *values):
1212 def debugconfig(ui, repo, *values):
1213 """show combined config settings from all hgrc files
1213 """show combined config settings from all hgrc files
1214
1214
1215 With no args, print names and values of all config items.
1215 With no args, print names and values of all config items.
1216
1216
1217 With one arg of the form section.name, print just the value of
1217 With one arg of the form section.name, print just the value of
1218 that config item.
1218 that config item.
1219
1219
1220 With multiple args, print names and values of all config items
1220 With multiple args, print names and values of all config items
1221 with matching section names."""
1221 with matching section names."""
1222
1222
1223 if values:
1223 if values:
1224 if len([v for v in values if '.' in v]) > 1:
1224 if len([v for v in values if '.' in v]) > 1:
1225 raise util.Abort(_('only one config item permitted'))
1225 raise util.Abort(_('only one config item permitted'))
1226 for section, name, value in ui.walkconfig():
1226 for section, name, value in ui.walkconfig():
1227 sectname = section + '.' + name
1227 sectname = section + '.' + name
1228 if values:
1228 if values:
1229 for v in values:
1229 for v in values:
1230 if v == section:
1230 if v == section:
1231 ui.write('%s=%s\n' % (sectname, value))
1231 ui.write('%s=%s\n' % (sectname, value))
1232 elif v == sectname:
1232 elif v == sectname:
1233 ui.write(value, '\n')
1233 ui.write(value, '\n')
1234 else:
1234 else:
1235 ui.write('%s=%s\n' % (sectname, value))
1235 ui.write('%s=%s\n' % (sectname, value))
1236
1236
1237 def debugsetparents(ui, repo, rev1, rev2=None):
1237 def debugsetparents(ui, repo, rev1, rev2=None):
1238 """manually set the parents of the current working directory
1238 """manually set the parents of the current working directory
1239
1239
1240 This is useful for writing repository conversion tools, but should
1240 This is useful for writing repository conversion tools, but should
1241 be used with care.
1241 be used with care.
1242 """
1242 """
1243
1243
1244 if not rev2:
1244 if not rev2:
1245 rev2 = hex(nullid)
1245 rev2 = hex(nullid)
1246
1246
1247 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1247 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1248
1248
1249 def debugstate(ui, repo):
1249 def debugstate(ui, repo):
1250 """show the contents of the current dirstate"""
1250 """show the contents of the current dirstate"""
1251 repo.dirstate.read()
1251 repo.dirstate.read()
1252 dc = repo.dirstate.map
1252 dc = repo.dirstate.map
1253 keys = dc.keys()
1253 keys = dc.keys()
1254 keys.sort()
1254 keys.sort()
1255 for file_ in keys:
1255 for file_ in keys:
1256 ui.write("%c %3o %10d %s %s\n"
1256 ui.write("%c %3o %10d %s %s\n"
1257 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1257 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1258 time.strftime("%x %X",
1258 time.strftime("%x %X",
1259 time.localtime(dc[file_][3])), file_))
1259 time.localtime(dc[file_][3])), file_))
1260 for f in repo.dirstate.copies:
1260 for f in repo.dirstate.copies:
1261 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1261 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1262
1262
1263 def debugdata(ui, file_, rev):
1263 def debugdata(ui, file_, rev):
1264 """dump the contents of an data file revision"""
1264 """dump the contents of an data file revision"""
1265 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1265 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1266 file_[:-2] + ".i", file_, 0)
1266 file_[:-2] + ".i", file_, 0)
1267 try:
1267 try:
1268 ui.write(r.revision(r.lookup(rev)))
1268 ui.write(r.revision(r.lookup(rev)))
1269 except KeyError:
1269 except KeyError:
1270 raise util.Abort(_('invalid revision identifier %s'), rev)
1270 raise util.Abort(_('invalid revision identifier %s'), rev)
1271
1271
1272 def debugindex(ui, file_):
1272 def debugindex(ui, file_):
1273 """dump the contents of an index file"""
1273 """dump the contents of an index file"""
1274 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1274 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1275 ui.write(" rev offset length base linkrev" +
1275 ui.write(" rev offset length base linkrev" +
1276 " nodeid p1 p2\n")
1276 " nodeid p1 p2\n")
1277 for i in range(r.count()):
1277 for i in range(r.count()):
1278 node = r.node(i)
1278 node = r.node(i)
1279 pp = r.parents(node)
1279 pp = r.parents(node)
1280 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1280 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1281 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1281 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1282 short(node), short(pp[0]), short(pp[1])))
1282 short(node), short(pp[0]), short(pp[1])))
1283
1283
1284 def debugindexdot(ui, file_):
1284 def debugindexdot(ui, file_):
1285 """dump an index DAG as a .dot file"""
1285 """dump an index DAG as a .dot file"""
1286 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1286 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1287 ui.write("digraph G {\n")
1287 ui.write("digraph G {\n")
1288 for i in range(r.count()):
1288 for i in range(r.count()):
1289 node = r.node(i)
1289 node = r.node(i)
1290 pp = r.parents(node)
1290 pp = r.parents(node)
1291 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1291 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1292 if pp[1] != nullid:
1292 if pp[1] != nullid:
1293 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1293 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1294 ui.write("}\n")
1294 ui.write("}\n")
1295
1295
1296 def debugrename(ui, repo, file, rev=None):
1296 def debugrename(ui, repo, file, rev=None):
1297 """dump rename information"""
1297 """dump rename information"""
1298 r = repo.file(relpath(repo, [file])[0])
1298 r = repo.file(relpath(repo, [file])[0])
1299 if rev:
1299 if rev:
1300 try:
1300 try:
1301 # assume all revision numbers are for changesets
1301 # assume all revision numbers are for changesets
1302 n = repo.lookup(rev)
1302 n = repo.lookup(rev)
1303 change = repo.changelog.read(n)
1303 change = repo.changelog.read(n)
1304 m = repo.manifest.read(change[0])
1304 m = repo.manifest.read(change[0])
1305 n = m[relpath(repo, [file])[0]]
1305 n = m[relpath(repo, [file])[0]]
1306 except (hg.RepoError, KeyError):
1306 except (hg.RepoError, KeyError):
1307 n = r.lookup(rev)
1307 n = r.lookup(rev)
1308 else:
1308 else:
1309 n = r.tip()
1309 n = r.tip()
1310 m = r.renamed(n)
1310 m = r.renamed(n)
1311 if m:
1311 if m:
1312 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1312 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1313 else:
1313 else:
1314 ui.write(_("not renamed\n"))
1314 ui.write(_("not renamed\n"))
1315
1315
1316 def debugwalk(ui, repo, *pats, **opts):
1316 def debugwalk(ui, repo, *pats, **opts):
1317 """show how files match on given patterns"""
1317 """show how files match on given patterns"""
1318 items = list(cmdutil.walk(repo, pats, opts))
1318 items = list(cmdutil.walk(repo, pats, opts))
1319 if not items:
1319 if not items:
1320 return
1320 return
1321 fmt = '%%s %%-%ds %%-%ds %%s' % (
1321 fmt = '%%s %%-%ds %%-%ds %%s' % (
1322 max([len(abs) for (src, abs, rel, exact) in items]),
1322 max([len(abs) for (src, abs, rel, exact) in items]),
1323 max([len(rel) for (src, abs, rel, exact) in items]))
1323 max([len(rel) for (src, abs, rel, exact) in items]))
1324 for src, abs, rel, exact in items:
1324 for src, abs, rel, exact in items:
1325 line = fmt % (src, abs, rel, exact and 'exact' or '')
1325 line = fmt % (src, abs, rel, exact and 'exact' or '')
1326 ui.write("%s\n" % line.rstrip())
1326 ui.write("%s\n" % line.rstrip())
1327
1327
1328 def diff(ui, repo, *pats, **opts):
1328 def diff(ui, repo, *pats, **opts):
1329 """diff repository (or selected files)
1329 """diff repository (or selected files)
1330
1330
1331 Show differences between revisions for the specified files.
1331 Show differences between revisions for the specified files.
1332
1332
1333 Differences between files are shown using the unified diff format.
1333 Differences between files are shown using the unified diff format.
1334
1334
1335 When two revision arguments are given, then changes are shown
1335 When two revision arguments are given, then changes are shown
1336 between those revisions. If only one revision is specified then
1336 between those revisions. If only one revision is specified then
1337 that revision is compared to the working directory, and, when no
1337 that revision is compared to the working directory, and, when no
1338 revisions are specified, the working directory files are compared
1338 revisions are specified, the working directory files are compared
1339 to its parent.
1339 to its parent.
1340
1340
1341 Without the -a option, diff will avoid generating diffs of files
1341 Without the -a option, diff will avoid generating diffs of files
1342 it detects as binary. With -a, diff will generate a diff anyway,
1342 it detects as binary. With -a, diff will generate a diff anyway,
1343 probably with undesirable results.
1343 probably with undesirable results.
1344 """
1344 """
1345 node1, node2 = revpair(ui, repo, opts['rev'])
1345 node1, node2 = revpair(ui, repo, opts['rev'])
1346
1346
1347 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1347 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1348
1348
1349 patch.diff(repo, node1, node2, fns, match=matchfn,
1349 patch.diff(repo, node1, node2, fns, match=matchfn,
1350 opts=patch.diffopts(ui, opts))
1350 opts=patch.diffopts(ui, opts))
1351
1351
1352 def export(ui, repo, *changesets, **opts):
1352 def export(ui, repo, *changesets, **opts):
1353 """dump the header and diffs for one or more changesets
1353 """dump the header and diffs for one or more changesets
1354
1354
1355 Print the changeset header and diffs for one or more revisions.
1355 Print the changeset header and diffs for one or more revisions.
1356
1356
1357 The information shown in the changeset header is: author,
1357 The information shown in the changeset header is: author,
1358 changeset hash, parent and commit comment.
1358 changeset hash, parent and commit comment.
1359
1359
1360 Output may be to a file, in which case the name of the file is
1360 Output may be to a file, in which case the name of the file is
1361 given using a format string. The formatting rules are as follows:
1361 given using a format string. The formatting rules are as follows:
1362
1362
1363 %% literal "%" character
1363 %% literal "%" character
1364 %H changeset hash (40 bytes of hexadecimal)
1364 %H changeset hash (40 bytes of hexadecimal)
1365 %N number of patches being generated
1365 %N number of patches being generated
1366 %R changeset revision number
1366 %R changeset revision number
1367 %b basename of the exporting repository
1367 %b basename of the exporting repository
1368 %h short-form changeset hash (12 bytes of hexadecimal)
1368 %h short-form changeset hash (12 bytes of hexadecimal)
1369 %n zero-padded sequence number, starting at 1
1369 %n zero-padded sequence number, starting at 1
1370 %r zero-padded changeset revision number
1370 %r zero-padded changeset revision number
1371
1371
1372 Without the -a option, export will avoid generating diffs of files
1372 Without the -a option, export will avoid generating diffs of files
1373 it detects as binary. With -a, export will generate a diff anyway,
1373 it detects as binary. With -a, export will generate a diff anyway,
1374 probably with undesirable results.
1374 probably with undesirable results.
1375
1375
1376 With the --switch-parent option, the diff will be against the second
1376 With the --switch-parent option, the diff will be against the second
1377 parent. It can be useful to review a merge.
1377 parent. It can be useful to review a merge.
1378 """
1378 """
1379 if not changesets:
1379 if not changesets:
1380 raise util.Abort(_("export requires at least one changeset"))
1380 raise util.Abort(_("export requires at least one changeset"))
1381 revs = list(revrange(ui, repo, changesets))
1381 revs = list(revrange(ui, repo, changesets))
1382 if len(revs) > 1:
1382 if len(revs) > 1:
1383 ui.note(_('exporting patches:\n'))
1383 ui.note(_('exporting patches:\n'))
1384 else:
1384 else:
1385 ui.note(_('exporting patch:\n'))
1385 ui.note(_('exporting patch:\n'))
1386 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1386 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1387 switch_parent=opts['switch_parent'],
1387 switch_parent=opts['switch_parent'],
1388 opts=patch.diffopts(ui, opts))
1388 opts=patch.diffopts(ui, opts))
1389
1389
1390 def forget(ui, repo, *pats, **opts):
1390 def forget(ui, repo, *pats, **opts):
1391 """don't add the specified files on the next commit (DEPRECATED)
1391 """don't add the specified files on the next commit (DEPRECATED)
1392
1392
1393 (DEPRECATED)
1393 (DEPRECATED)
1394 Undo an 'hg add' scheduled for the next commit.
1394 Undo an 'hg add' scheduled for the next commit.
1395
1395
1396 This command is now deprecated and will be removed in a future
1396 This command is now deprecated and will be removed in a future
1397 release. Please use revert instead.
1397 release. Please use revert instead.
1398 """
1398 """
1399 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1399 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1400 forget = []
1400 forget = []
1401 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1401 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1402 if repo.dirstate.state(abs) == 'a':
1402 if repo.dirstate.state(abs) == 'a':
1403 forget.append(abs)
1403 forget.append(abs)
1404 if ui.verbose or not exact:
1404 if ui.verbose or not exact:
1405 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1405 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1406 repo.forget(forget)
1406 repo.forget(forget)
1407
1407
1408 def grep(ui, repo, pattern, *pats, **opts):
1408 def grep(ui, repo, pattern, *pats, **opts):
1409 """search for a pattern in specified files and revisions
1409 """search for a pattern in specified files and revisions
1410
1410
1411 Search revisions of files for a regular expression.
1411 Search revisions of files for a regular expression.
1412
1412
1413 This command behaves differently than Unix grep. It only accepts
1413 This command behaves differently than Unix grep. It only accepts
1414 Python/Perl regexps. It searches repository history, not the
1414 Python/Perl regexps. It searches repository history, not the
1415 working directory. It always prints the revision number in which
1415 working directory. It always prints the revision number in which
1416 a match appears.
1416 a match appears.
1417
1417
1418 By default, grep only prints output for the first revision of a
1418 By default, grep only prints output for the first revision of a
1419 file in which it finds a match. To get it to print every revision
1419 file in which it finds a match. To get it to print every revision
1420 that contains a change in match status ("-" for a match that
1420 that contains a change in match status ("-" for a match that
1421 becomes a non-match, or "+" for a non-match that becomes a match),
1421 becomes a non-match, or "+" for a non-match that becomes a match),
1422 use the --all flag.
1422 use the --all flag.
1423 """
1423 """
1424 reflags = 0
1424 reflags = 0
1425 if opts['ignore_case']:
1425 if opts['ignore_case']:
1426 reflags |= re.I
1426 reflags |= re.I
1427 regexp = re.compile(pattern, reflags)
1427 regexp = re.compile(pattern, reflags)
1428 sep, eol = ':', '\n'
1428 sep, eol = ':', '\n'
1429 if opts['print0']:
1429 if opts['print0']:
1430 sep = eol = '\0'
1430 sep = eol = '\0'
1431
1431
1432 fcache = {}
1432 fcache = {}
1433 def getfile(fn):
1433 def getfile(fn):
1434 if fn not in fcache:
1434 if fn not in fcache:
1435 fcache[fn] = repo.file(fn)
1435 fcache[fn] = repo.file(fn)
1436 return fcache[fn]
1436 return fcache[fn]
1437
1437
1438 def matchlines(body):
1438 def matchlines(body):
1439 begin = 0
1439 begin = 0
1440 linenum = 0
1440 linenum = 0
1441 while True:
1441 while True:
1442 match = regexp.search(body, begin)
1442 match = regexp.search(body, begin)
1443 if not match:
1443 if not match:
1444 break
1444 break
1445 mstart, mend = match.span()
1445 mstart, mend = match.span()
1446 linenum += body.count('\n', begin, mstart) + 1
1446 linenum += body.count('\n', begin, mstart) + 1
1447 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1447 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1448 lend = body.find('\n', mend)
1448 lend = body.find('\n', mend)
1449 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1449 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1450 begin = lend + 1
1450 begin = lend + 1
1451
1451
1452 class linestate(object):
1452 class linestate(object):
1453 def __init__(self, line, linenum, colstart, colend):
1453 def __init__(self, line, linenum, colstart, colend):
1454 self.line = line
1454 self.line = line
1455 self.linenum = linenum
1455 self.linenum = linenum
1456 self.colstart = colstart
1456 self.colstart = colstart
1457 self.colend = colend
1457 self.colend = colend
1458
1458
1459 def __eq__(self, other):
1459 def __eq__(self, other):
1460 return self.line == other.line
1460 return self.line == other.line
1461
1461
1462 matches = {}
1462 matches = {}
1463 copies = {}
1463 copies = {}
1464 def grepbody(fn, rev, body):
1464 def grepbody(fn, rev, body):
1465 matches[rev].setdefault(fn, [])
1465 matches[rev].setdefault(fn, [])
1466 m = matches[rev][fn]
1466 m = matches[rev][fn]
1467 for lnum, cstart, cend, line in matchlines(body):
1467 for lnum, cstart, cend, line in matchlines(body):
1468 s = linestate(line, lnum, cstart, cend)
1468 s = linestate(line, lnum, cstart, cend)
1469 m.append(s)
1469 m.append(s)
1470
1470
1471 def difflinestates(a, b):
1471 def difflinestates(a, b):
1472 sm = difflib.SequenceMatcher(None, a, b)
1472 sm = difflib.SequenceMatcher(None, a, b)
1473 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1473 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1474 if tag == 'insert':
1474 if tag == 'insert':
1475 for i in range(blo, bhi):
1475 for i in range(blo, bhi):
1476 yield ('+', b[i])
1476 yield ('+', b[i])
1477 elif tag == 'delete':
1477 elif tag == 'delete':
1478 for i in range(alo, ahi):
1478 for i in range(alo, ahi):
1479 yield ('-', a[i])
1479 yield ('-', a[i])
1480 elif tag == 'replace':
1480 elif tag == 'replace':
1481 for i in range(alo, ahi):
1481 for i in range(alo, ahi):
1482 yield ('-', a[i])
1482 yield ('-', a[i])
1483 for i in range(blo, bhi):
1483 for i in range(blo, bhi):
1484 yield ('+', b[i])
1484 yield ('+', b[i])
1485
1485
1486 prev = {}
1486 prev = {}
1487 ucache = {}
1487 ucache = {}
1488 def display(fn, rev, states, prevstates):
1488 def display(fn, rev, states, prevstates):
1489 counts = {'-': 0, '+': 0}
1489 counts = {'-': 0, '+': 0}
1490 filerevmatches = {}
1490 filerevmatches = {}
1491 if incrementing or not opts['all']:
1491 if incrementing or not opts['all']:
1492 a, b = prevstates, states
1492 a, b = prevstates, states
1493 else:
1493 else:
1494 a, b = states, prevstates
1494 a, b = states, prevstates
1495 for change, l in difflinestates(a, b):
1495 for change, l in difflinestates(a, b):
1496 if incrementing or not opts['all']:
1496 if incrementing or not opts['all']:
1497 r = rev
1497 r = rev
1498 else:
1498 else:
1499 r = prev[fn]
1499 r = prev[fn]
1500 cols = [fn, str(r)]
1500 cols = [fn, str(r)]
1501 if opts['line_number']:
1501 if opts['line_number']:
1502 cols.append(str(l.linenum))
1502 cols.append(str(l.linenum))
1503 if opts['all']:
1503 if opts['all']:
1504 cols.append(change)
1504 cols.append(change)
1505 if opts['user']:
1505 if opts['user']:
1506 cols.append(trimuser(ui, getchange(r)[1], rev,
1506 cols.append(trimuser(ui, getchange(r)[1], rev,
1507 ucache))
1507 ucache))
1508 if opts['files_with_matches']:
1508 if opts['files_with_matches']:
1509 c = (fn, rev)
1509 c = (fn, rev)
1510 if c in filerevmatches:
1510 if c in filerevmatches:
1511 continue
1511 continue
1512 filerevmatches[c] = 1
1512 filerevmatches[c] = 1
1513 else:
1513 else:
1514 cols.append(l.line)
1514 cols.append(l.line)
1515 ui.write(sep.join(cols), eol)
1515 ui.write(sep.join(cols), eol)
1516 counts[change] += 1
1516 counts[change] += 1
1517 return counts['+'], counts['-']
1517 return counts['+'], counts['-']
1518
1518
1519 fstate = {}
1519 fstate = {}
1520 skip = {}
1520 skip = {}
1521 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1521 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1522 count = 0
1522 count = 0
1523 incrementing = False
1523 incrementing = False
1524 follow = opts.get('follow')
1524 follow = opts.get('follow')
1525 for st, rev, fns in changeiter:
1525 for st, rev, fns in changeiter:
1526 if st == 'window':
1526 if st == 'window':
1527 incrementing = rev
1527 incrementing = rev
1528 matches.clear()
1528 matches.clear()
1529 copies.clear()
1530 elif st == 'add':
1529 elif st == 'add':
1531 change = repo.changelog.read(repo.lookup(str(rev)))
1530 change = repo.changelog.read(repo.lookup(str(rev)))
1532 mf = repo.manifest.read(change[0])
1531 mf = repo.manifest.read(change[0])
1533 matches[rev] = {}
1532 matches[rev] = {}
1534 for fn in fns:
1533 for fn in fns:
1535 if fn in skip:
1534 if fn in skip:
1536 continue
1535 continue
1537 fstate.setdefault(fn, {})
1536 fstate.setdefault(fn, {})
1538 copies.setdefault(rev, {})
1539 try:
1537 try:
1540 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1538 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1541 if follow:
1539 if follow:
1542 copied = getfile(fn).renamed(mf[fn])
1540 copied = getfile(fn).renamed(mf[fn])
1543 if copied:
1541 if copied:
1544 copies[rev][fn] = copied[0]
1542 copies.setdefault(rev, {})[fn] = copied[0]
1545 except KeyError:
1543 except KeyError:
1546 pass
1544 pass
1547 elif st == 'iter':
1545 elif st == 'iter':
1548 states = matches[rev].items()
1546 states = matches[rev].items()
1549 states.sort()
1547 states.sort()
1550 for fn, m in states:
1548 for fn, m in states:
1551 copy = copies[rev].get(fn)
1549 copy = copies.get(rev, {}).get(fn)
1552 if fn in skip:
1550 if fn in skip:
1553 if copy:
1551 if copy:
1554 skip[copy] = True
1552 skip[copy] = True
1555 continue
1553 continue
1556 if incrementing or not opts['all'] or fstate[fn]:
1554 if incrementing or not opts['all'] or fstate[fn]:
1557 pos, neg = display(fn, rev, m, fstate[fn])
1555 pos, neg = display(fn, rev, m, fstate[fn])
1558 count += pos + neg
1556 count += pos + neg
1559 if pos and not opts['all']:
1557 if pos and not opts['all']:
1560 skip[fn] = True
1558 skip[fn] = True
1561 if copy:
1559 if copy:
1562 skip[copy] = True
1560 skip[copy] = True
1563 fstate[fn] = m
1561 fstate[fn] = m
1564 if copy:
1562 if copy:
1565 fstate[copy] = m
1563 fstate[copy] = m
1566 prev[fn] = rev
1564 prev[fn] = rev
1567
1565
1568 if not incrementing:
1566 if not incrementing:
1569 fstate = fstate.items()
1567 fstate = fstate.items()
1570 fstate.sort()
1568 fstate.sort()
1571 for fn, state in fstate:
1569 for fn, state in fstate:
1572 if fn in skip:
1570 if fn in skip:
1573 continue
1571 continue
1574 if fn not in copies[prev[fn]]:
1572 if fn not in copies.get(prev[fn], {}):
1575 display(fn, rev, {}, state)
1573 display(fn, rev, {}, state)
1576 return (count == 0 and 1) or 0
1574 return (count == 0 and 1) or 0
1577
1575
1578 def heads(ui, repo, **opts):
1576 def heads(ui, repo, **opts):
1579 """show current repository heads
1577 """show current repository heads
1580
1578
1581 Show all repository head changesets.
1579 Show all repository head changesets.
1582
1580
1583 Repository "heads" are changesets that don't have children
1581 Repository "heads" are changesets that don't have children
1584 changesets. They are where development generally takes place and
1582 changesets. They are where development generally takes place and
1585 are the usual targets for update and merge operations.
1583 are the usual targets for update and merge operations.
1586 """
1584 """
1587 if opts['rev']:
1585 if opts['rev']:
1588 heads = repo.heads(repo.lookup(opts['rev']))
1586 heads = repo.heads(repo.lookup(opts['rev']))
1589 else:
1587 else:
1590 heads = repo.heads()
1588 heads = repo.heads()
1591 br = None
1589 br = None
1592 if opts['branches']:
1590 if opts['branches']:
1593 br = repo.branchlookup(heads)
1591 br = repo.branchlookup(heads)
1594 displayer = show_changeset(ui, repo, opts)
1592 displayer = show_changeset(ui, repo, opts)
1595 for n in heads:
1593 for n in heads:
1596 displayer.show(changenode=n, brinfo=br)
1594 displayer.show(changenode=n, brinfo=br)
1597
1595
1598 def identify(ui, repo):
1596 def identify(ui, repo):
1599 """print information about the working copy
1597 """print information about the working copy
1600
1598
1601 Print a short summary of the current state of the repo.
1599 Print a short summary of the current state of the repo.
1602
1600
1603 This summary identifies the repository state using one or two parent
1601 This summary identifies the repository state using one or two parent
1604 hash identifiers, followed by a "+" if there are uncommitted changes
1602 hash identifiers, followed by a "+" if there are uncommitted changes
1605 in the working directory, followed by a list of tags for this revision.
1603 in the working directory, followed by a list of tags for this revision.
1606 """
1604 """
1607 parents = [p for p in repo.dirstate.parents() if p != nullid]
1605 parents = [p for p in repo.dirstate.parents() if p != nullid]
1608 if not parents:
1606 if not parents:
1609 ui.write(_("unknown\n"))
1607 ui.write(_("unknown\n"))
1610 return
1608 return
1611
1609
1612 hexfunc = ui.verbose and hex or short
1610 hexfunc = ui.verbose and hex or short
1613 modified, added, removed, deleted = repo.status()[:4]
1611 modified, added, removed, deleted = repo.status()[:4]
1614 output = ["%s%s" %
1612 output = ["%s%s" %
1615 ('+'.join([hexfunc(parent) for parent in parents]),
1613 ('+'.join([hexfunc(parent) for parent in parents]),
1616 (modified or added or removed or deleted) and "+" or "")]
1614 (modified or added or removed or deleted) and "+" or "")]
1617
1615
1618 if not ui.quiet:
1616 if not ui.quiet:
1619 # multiple tags for a single parent separated by '/'
1617 # multiple tags for a single parent separated by '/'
1620 parenttags = ['/'.join(tags)
1618 parenttags = ['/'.join(tags)
1621 for tags in map(repo.nodetags, parents) if tags]
1619 for tags in map(repo.nodetags, parents) if tags]
1622 # tags for multiple parents separated by ' + '
1620 # tags for multiple parents separated by ' + '
1623 if parenttags:
1621 if parenttags:
1624 output.append(' + '.join(parenttags))
1622 output.append(' + '.join(parenttags))
1625
1623
1626 ui.write("%s\n" % ' '.join(output))
1624 ui.write("%s\n" % ' '.join(output))
1627
1625
1628 def import_(ui, repo, patch1, *patches, **opts):
1626 def import_(ui, repo, patch1, *patches, **opts):
1629 """import an ordered set of patches
1627 """import an ordered set of patches
1630
1628
1631 Import a list of patches and commit them individually.
1629 Import a list of patches and commit them individually.
1632
1630
1633 If there are outstanding changes in the working directory, import
1631 If there are outstanding changes in the working directory, import
1634 will abort unless given the -f flag.
1632 will abort unless given the -f flag.
1635
1633
1636 You can import a patch straight from a mail message. Even patches
1634 You can import a patch straight from a mail message. Even patches
1637 as attachments work (body part must be type text/plain or
1635 as attachments work (body part must be type text/plain or
1638 text/x-patch to be used). From and Subject headers of email
1636 text/x-patch to be used). From and Subject headers of email
1639 message are used as default committer and commit message. All
1637 message are used as default committer and commit message. All
1640 text/plain body parts before first diff are added to commit
1638 text/plain body parts before first diff are added to commit
1641 message.
1639 message.
1642
1640
1643 If imported patch was generated by hg export, user and description
1641 If imported patch was generated by hg export, user and description
1644 from patch override values from message headers and body. Values
1642 from patch override values from message headers and body. Values
1645 given on command line with -m and -u override these.
1643 given on command line with -m and -u override these.
1646
1644
1647 To read a patch from standard input, use patch name "-".
1645 To read a patch from standard input, use patch name "-".
1648 """
1646 """
1649 patches = (patch1,) + patches
1647 patches = (patch1,) + patches
1650
1648
1651 if not opts['force']:
1649 if not opts['force']:
1652 bail_if_changed(repo)
1650 bail_if_changed(repo)
1653
1651
1654 d = opts["base"]
1652 d = opts["base"]
1655 strip = opts["strip"]
1653 strip = opts["strip"]
1656
1654
1657 wlock = repo.wlock()
1655 wlock = repo.wlock()
1658 lock = repo.lock()
1656 lock = repo.lock()
1659
1657
1660 for p in patches:
1658 for p in patches:
1661 pf = os.path.join(d, p)
1659 pf = os.path.join(d, p)
1662
1660
1663 if pf == '-':
1661 if pf == '-':
1664 ui.status(_("applying patch from stdin\n"))
1662 ui.status(_("applying patch from stdin\n"))
1665 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1663 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1666 else:
1664 else:
1667 ui.status(_("applying %s\n") % p)
1665 ui.status(_("applying %s\n") % p)
1668 tmpname, message, user, date = patch.extract(ui, file(pf))
1666 tmpname, message, user, date = patch.extract(ui, file(pf))
1669
1667
1670 if tmpname is None:
1668 if tmpname is None:
1671 raise util.Abort(_('no diffs found'))
1669 raise util.Abort(_('no diffs found'))
1672
1670
1673 try:
1671 try:
1674 if opts['message']:
1672 if opts['message']:
1675 # pickup the cmdline msg
1673 # pickup the cmdline msg
1676 message = opts['message']
1674 message = opts['message']
1677 elif message:
1675 elif message:
1678 # pickup the patch msg
1676 # pickup the patch msg
1679 message = message.strip()
1677 message = message.strip()
1680 else:
1678 else:
1681 # launch the editor
1679 # launch the editor
1682 message = None
1680 message = None
1683 ui.debug(_('message:\n%s\n') % message)
1681 ui.debug(_('message:\n%s\n') % message)
1684
1682
1685 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1683 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1686 removes = []
1684 files = patch.updatedir(ui, repo, files, wlock=wlock)
1687 if len(files) > 0:
1688 cfiles = files.keys()
1689 copies = []
1690 copts = {'after': False, 'force': False}
1691 cwd = repo.getcwd()
1692 if cwd:
1693 cfiles = [util.pathto(cwd, f) for f in files.keys()]
1694 for f in files:
1695 ctype, gp = files[f]
1696 if ctype == 'RENAME':
1697 copies.append((gp.oldpath, gp.path, gp.copymod))
1698 removes.append(gp.oldpath)
1699 elif ctype == 'COPY':
1700 copies.append((gp.oldpath, gp.path, gp.copymod))
1701 elif ctype == 'DELETE':
1702 removes.append(gp.path)
1703 for src, dst, after in copies:
1704 absdst = os.path.join(repo.root, dst)
1705 if not after and os.path.exists(absdst):
1706 raise util.Abort(_('patch creates existing file %s') % dst)
1707 if cwd:
1708 src, dst = [util.pathto(cwd, f) for f in (src, dst)]
1709 copts['after'] = after
1710 errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock)
1711 if errs:
1712 raise util.Abort(errs)
1713 if removes:
1714 repo.remove(removes, True, wlock=wlock)
1715 for f in files:
1716 ctype, gp = files[f]
1717 if gp and gp.mode:
1718 x = gp.mode & 0100 != 0
1719 dst = os.path.join(repo.root, gp.path)
1720 util.set_exec(dst, x)
1721 cmdutil.addremove(repo, cfiles, wlock=wlock)
1722 files = files.keys()
1723 files.extend([r for r in removes if r not in files])
1724 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1685 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1725 finally:
1686 finally:
1726 os.unlink(tmpname)
1687 os.unlink(tmpname)
1727
1688
1728 def incoming(ui, repo, source="default", **opts):
1689 def incoming(ui, repo, source="default", **opts):
1729 """show new changesets found in source
1690 """show new changesets found in source
1730
1691
1731 Show new changesets found in the specified path/URL or the default
1692 Show new changesets found in the specified path/URL or the default
1732 pull location. These are the changesets that would be pulled if a pull
1693 pull location. These are the changesets that would be pulled if a pull
1733 was requested.
1694 was requested.
1734
1695
1735 For remote repository, using --bundle avoids downloading the changesets
1696 For remote repository, using --bundle avoids downloading the changesets
1736 twice if the incoming is followed by a pull.
1697 twice if the incoming is followed by a pull.
1737
1698
1738 See pull for valid source format details.
1699 See pull for valid source format details.
1739 """
1700 """
1740 source = ui.expandpath(source)
1701 source = ui.expandpath(source)
1741 setremoteconfig(ui, opts)
1702 setremoteconfig(ui, opts)
1742
1703
1743 other = hg.repository(ui, source)
1704 other = hg.repository(ui, source)
1744 incoming = repo.findincoming(other, force=opts["force"])
1705 incoming = repo.findincoming(other, force=opts["force"])
1745 if not incoming:
1706 if not incoming:
1746 ui.status(_("no changes found\n"))
1707 ui.status(_("no changes found\n"))
1747 return
1708 return
1748
1709
1749 cleanup = None
1710 cleanup = None
1750 try:
1711 try:
1751 fname = opts["bundle"]
1712 fname = opts["bundle"]
1752 if fname or not other.local():
1713 if fname or not other.local():
1753 # create a bundle (uncompressed if other repo is not local)
1714 # create a bundle (uncompressed if other repo is not local)
1754 cg = other.changegroup(incoming, "incoming")
1715 cg = other.changegroup(incoming, "incoming")
1755 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1716 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1756 # keep written bundle?
1717 # keep written bundle?
1757 if opts["bundle"]:
1718 if opts["bundle"]:
1758 cleanup = None
1719 cleanup = None
1759 if not other.local():
1720 if not other.local():
1760 # use the created uncompressed bundlerepo
1721 # use the created uncompressed bundlerepo
1761 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1722 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1762
1723
1763 revs = None
1724 revs = None
1764 if opts['rev']:
1725 if opts['rev']:
1765 revs = [other.lookup(rev) for rev in opts['rev']]
1726 revs = [other.lookup(rev) for rev in opts['rev']]
1766 o = other.changelog.nodesbetween(incoming, revs)[0]
1727 o = other.changelog.nodesbetween(incoming, revs)[0]
1767 if opts['newest_first']:
1728 if opts['newest_first']:
1768 o.reverse()
1729 o.reverse()
1769 displayer = show_changeset(ui, other, opts)
1730 displayer = show_changeset(ui, other, opts)
1770 for n in o:
1731 for n in o:
1771 parents = [p for p in other.changelog.parents(n) if p != nullid]
1732 parents = [p for p in other.changelog.parents(n) if p != nullid]
1772 if opts['no_merges'] and len(parents) == 2:
1733 if opts['no_merges'] and len(parents) == 2:
1773 continue
1734 continue
1774 displayer.show(changenode=n)
1735 displayer.show(changenode=n)
1775 if opts['patch']:
1736 if opts['patch']:
1776 prev = (parents and parents[0]) or nullid
1737 prev = (parents and parents[0]) or nullid
1777 patch.diff(repo, other, prev, n)
1738 patch.diff(repo, other, prev, n)
1778 ui.write("\n")
1739 ui.write("\n")
1779 finally:
1740 finally:
1780 if hasattr(other, 'close'):
1741 if hasattr(other, 'close'):
1781 other.close()
1742 other.close()
1782 if cleanup:
1743 if cleanup:
1783 os.unlink(cleanup)
1744 os.unlink(cleanup)
1784
1745
1785 def init(ui, dest=".", **opts):
1746 def init(ui, dest=".", **opts):
1786 """create a new repository in the given directory
1747 """create a new repository in the given directory
1787
1748
1788 Initialize a new repository in the given directory. If the given
1749 Initialize a new repository in the given directory. If the given
1789 directory does not exist, it is created.
1750 directory does not exist, it is created.
1790
1751
1791 If no directory is given, the current directory is used.
1752 If no directory is given, the current directory is used.
1792
1753
1793 It is possible to specify an ssh:// URL as the destination.
1754 It is possible to specify an ssh:// URL as the destination.
1794 Look at the help text for the pull command for important details
1755 Look at the help text for the pull command for important details
1795 about ssh:// URLs.
1756 about ssh:// URLs.
1796 """
1757 """
1797 setremoteconfig(ui, opts)
1758 setremoteconfig(ui, opts)
1798 hg.repository(ui, dest, create=1)
1759 hg.repository(ui, dest, create=1)
1799
1760
1800 def locate(ui, repo, *pats, **opts):
1761 def locate(ui, repo, *pats, **opts):
1801 """locate files matching specific patterns
1762 """locate files matching specific patterns
1802
1763
1803 Print all files under Mercurial control whose names match the
1764 Print all files under Mercurial control whose names match the
1804 given patterns.
1765 given patterns.
1805
1766
1806 This command searches the current directory and its
1767 This command searches the current directory and its
1807 subdirectories. To search an entire repository, move to the root
1768 subdirectories. To search an entire repository, move to the root
1808 of the repository.
1769 of the repository.
1809
1770
1810 If no patterns are given to match, this command prints all file
1771 If no patterns are given to match, this command prints all file
1811 names.
1772 names.
1812
1773
1813 If you want to feed the output of this command into the "xargs"
1774 If you want to feed the output of this command into the "xargs"
1814 command, use the "-0" option to both this command and "xargs".
1775 command, use the "-0" option to both this command and "xargs".
1815 This will avoid the problem of "xargs" treating single filenames
1776 This will avoid the problem of "xargs" treating single filenames
1816 that contain white space as multiple filenames.
1777 that contain white space as multiple filenames.
1817 """
1778 """
1818 end = opts['print0'] and '\0' or '\n'
1779 end = opts['print0'] and '\0' or '\n'
1819 rev = opts['rev']
1780 rev = opts['rev']
1820 if rev:
1781 if rev:
1821 node = repo.lookup(rev)
1782 node = repo.lookup(rev)
1822 else:
1783 else:
1823 node = None
1784 node = None
1824
1785
1825 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1786 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1826 head='(?:.*/|)'):
1787 head='(?:.*/|)'):
1827 if not node and repo.dirstate.state(abs) == '?':
1788 if not node and repo.dirstate.state(abs) == '?':
1828 continue
1789 continue
1829 if opts['fullpath']:
1790 if opts['fullpath']:
1830 ui.write(os.path.join(repo.root, abs), end)
1791 ui.write(os.path.join(repo.root, abs), end)
1831 else:
1792 else:
1832 ui.write(((pats and rel) or abs), end)
1793 ui.write(((pats and rel) or abs), end)
1833
1794
1834 def log(ui, repo, *pats, **opts):
1795 def log(ui, repo, *pats, **opts):
1835 """show revision history of entire repository or files
1796 """show revision history of entire repository or files
1836
1797
1837 Print the revision history of the specified files or the entire
1798 Print the revision history of the specified files or the entire
1838 project.
1799 project.
1839
1800
1840 File history is shown without following rename or copy history of
1801 File history is shown without following rename or copy history of
1841 files. Use -f/--follow with a file name to follow history across
1802 files. Use -f/--follow with a file name to follow history across
1842 renames and copies. --follow without a file name will only show
1803 renames and copies. --follow without a file name will only show
1843 ancestors or descendants of the starting revision. --follow-first
1804 ancestors or descendants of the starting revision. --follow-first
1844 only follows the first parent of merge revisions.
1805 only follows the first parent of merge revisions.
1845
1806
1846 If no revision range is specified, the default is tip:0 unless
1807 If no revision range is specified, the default is tip:0 unless
1847 --follow is set, in which case the working directory parent is
1808 --follow is set, in which case the working directory parent is
1848 used as the starting revision.
1809 used as the starting revision.
1849
1810
1850 By default this command outputs: changeset id and hash, tags,
1811 By default this command outputs: changeset id and hash, tags,
1851 non-trivial parents, user, date and time, and a summary for each
1812 non-trivial parents, user, date and time, and a summary for each
1852 commit. When the -v/--verbose switch is used, the list of changed
1813 commit. When the -v/--verbose switch is used, the list of changed
1853 files and full commit message is shown.
1814 files and full commit message is shown.
1854 """
1815 """
1855 class dui(object):
1816 class dui(object):
1856 # Implement and delegate some ui protocol. Save hunks of
1817 # Implement and delegate some ui protocol. Save hunks of
1857 # output for later display in the desired order.
1818 # output for later display in the desired order.
1858 def __init__(self, ui):
1819 def __init__(self, ui):
1859 self.ui = ui
1820 self.ui = ui
1860 self.hunk = {}
1821 self.hunk = {}
1861 self.header = {}
1822 self.header = {}
1862 def bump(self, rev):
1823 def bump(self, rev):
1863 self.rev = rev
1824 self.rev = rev
1864 self.hunk[rev] = []
1825 self.hunk[rev] = []
1865 self.header[rev] = []
1826 self.header[rev] = []
1866 def note(self, *args):
1827 def note(self, *args):
1867 if self.verbose:
1828 if self.verbose:
1868 self.write(*args)
1829 self.write(*args)
1869 def status(self, *args):
1830 def status(self, *args):
1870 if not self.quiet:
1831 if not self.quiet:
1871 self.write(*args)
1832 self.write(*args)
1872 def write(self, *args):
1833 def write(self, *args):
1873 self.hunk[self.rev].append(args)
1834 self.hunk[self.rev].append(args)
1874 def write_header(self, *args):
1835 def write_header(self, *args):
1875 self.header[self.rev].append(args)
1836 self.header[self.rev].append(args)
1876 def debug(self, *args):
1837 def debug(self, *args):
1877 if self.debugflag:
1838 if self.debugflag:
1878 self.write(*args)
1839 self.write(*args)
1879 def __getattr__(self, key):
1840 def __getattr__(self, key):
1880 return getattr(self.ui, key)
1841 return getattr(self.ui, key)
1881
1842
1882 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1843 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1883
1844
1884 if opts['limit']:
1845 if opts['limit']:
1885 try:
1846 try:
1886 limit = int(opts['limit'])
1847 limit = int(opts['limit'])
1887 except ValueError:
1848 except ValueError:
1888 raise util.Abort(_('limit must be a positive integer'))
1849 raise util.Abort(_('limit must be a positive integer'))
1889 if limit <= 0: raise util.Abort(_('limit must be positive'))
1850 if limit <= 0: raise util.Abort(_('limit must be positive'))
1890 else:
1851 else:
1891 limit = sys.maxint
1852 limit = sys.maxint
1892 count = 0
1853 count = 0
1893
1854
1894 displayer = show_changeset(ui, repo, opts)
1855 displayer = show_changeset(ui, repo, opts)
1895 for st, rev, fns in changeiter:
1856 for st, rev, fns in changeiter:
1896 if st == 'window':
1857 if st == 'window':
1897 du = dui(ui)
1858 du = dui(ui)
1898 displayer.ui = du
1859 displayer.ui = du
1899 elif st == 'add':
1860 elif st == 'add':
1900 du.bump(rev)
1861 du.bump(rev)
1901 changenode = repo.changelog.node(rev)
1862 changenode = repo.changelog.node(rev)
1902 parents = [p for p in repo.changelog.parents(changenode)
1863 parents = [p for p in repo.changelog.parents(changenode)
1903 if p != nullid]
1864 if p != nullid]
1904 if opts['no_merges'] and len(parents) == 2:
1865 if opts['no_merges'] and len(parents) == 2:
1905 continue
1866 continue
1906 if opts['only_merges'] and len(parents) != 2:
1867 if opts['only_merges'] and len(parents) != 2:
1907 continue
1868 continue
1908
1869
1909 if opts['keyword']:
1870 if opts['keyword']:
1910 changes = getchange(rev)
1871 changes = getchange(rev)
1911 miss = 0
1872 miss = 0
1912 for k in [kw.lower() for kw in opts['keyword']]:
1873 for k in [kw.lower() for kw in opts['keyword']]:
1913 if not (k in changes[1].lower() or
1874 if not (k in changes[1].lower() or
1914 k in changes[4].lower() or
1875 k in changes[4].lower() or
1915 k in " ".join(changes[3][:20]).lower()):
1876 k in " ".join(changes[3][:20]).lower()):
1916 miss = 1
1877 miss = 1
1917 break
1878 break
1918 if miss:
1879 if miss:
1919 continue
1880 continue
1920
1881
1921 br = None
1882 br = None
1922 if opts['branches']:
1883 if opts['branches']:
1923 br = repo.branchlookup([repo.changelog.node(rev)])
1884 br = repo.branchlookup([repo.changelog.node(rev)])
1924
1885
1925 displayer.show(rev, brinfo=br)
1886 displayer.show(rev, brinfo=br)
1926 if opts['patch']:
1887 if opts['patch']:
1927 prev = (parents and parents[0]) or nullid
1888 prev = (parents and parents[0]) or nullid
1928 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1889 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1929 du.write("\n\n")
1890 du.write("\n\n")
1930 elif st == 'iter':
1891 elif st == 'iter':
1931 if count == limit: break
1892 if count == limit: break
1932 if du.header[rev]:
1893 if du.header[rev]:
1933 for args in du.header[rev]:
1894 for args in du.header[rev]:
1934 ui.write_header(*args)
1895 ui.write_header(*args)
1935 if du.hunk[rev]:
1896 if du.hunk[rev]:
1936 count += 1
1897 count += 1
1937 for args in du.hunk[rev]:
1898 for args in du.hunk[rev]:
1938 ui.write(*args)
1899 ui.write(*args)
1939
1900
1940 def manifest(ui, repo, rev=None):
1901 def manifest(ui, repo, rev=None):
1941 """output the latest or given revision of the project manifest
1902 """output the latest or given revision of the project manifest
1942
1903
1943 Print a list of version controlled files for the given revision.
1904 Print a list of version controlled files for the given revision.
1944
1905
1945 The manifest is the list of files being version controlled. If no revision
1906 The manifest is the list of files being version controlled. If no revision
1946 is given then the tip is used.
1907 is given then the tip is used.
1947 """
1908 """
1948 if rev:
1909 if rev:
1949 try:
1910 try:
1950 # assume all revision numbers are for changesets
1911 # assume all revision numbers are for changesets
1951 n = repo.lookup(rev)
1912 n = repo.lookup(rev)
1952 change = repo.changelog.read(n)
1913 change = repo.changelog.read(n)
1953 n = change[0]
1914 n = change[0]
1954 except hg.RepoError:
1915 except hg.RepoError:
1955 n = repo.manifest.lookup(rev)
1916 n = repo.manifest.lookup(rev)
1956 else:
1917 else:
1957 n = repo.manifest.tip()
1918 n = repo.manifest.tip()
1958 m = repo.manifest.read(n)
1919 m = repo.manifest.read(n)
1959 files = m.keys()
1920 files = m.keys()
1960 files.sort()
1921 files.sort()
1961
1922
1962 for f in files:
1923 for f in files:
1963 ui.write("%40s %3s %s\n" % (hex(m[f]),
1924 ui.write("%40s %3s %s\n" % (hex(m[f]),
1964 m.execf(f) and "755" or "644", f))
1925 m.execf(f) and "755" or "644", f))
1965
1926
1966 def merge(ui, repo, node=None, force=None, branch=None):
1927 def merge(ui, repo, node=None, force=None, branch=None):
1967 """Merge working directory with another revision
1928 """Merge working directory with another revision
1968
1929
1969 Merge the contents of the current working directory and the
1930 Merge the contents of the current working directory and the
1970 requested revision. Files that changed between either parent are
1931 requested revision. Files that changed between either parent are
1971 marked as changed for the next commit and a commit must be
1932 marked as changed for the next commit and a commit must be
1972 performed before any further updates are allowed.
1933 performed before any further updates are allowed.
1973
1934
1974 If no revision is specified, the working directory's parent is a
1935 If no revision is specified, the working directory's parent is a
1975 head revision, and the repository contains exactly one other head,
1936 head revision, and the repository contains exactly one other head,
1976 the other head is merged with by default. Otherwise, an explicit
1937 the other head is merged with by default. Otherwise, an explicit
1977 revision to merge with must be provided.
1938 revision to merge with must be provided.
1978 """
1939 """
1979
1940
1980 if node:
1941 if node:
1981 node = _lookup(repo, node, branch)
1942 node = _lookup(repo, node, branch)
1982 else:
1943 else:
1983 heads = repo.heads()
1944 heads = repo.heads()
1984 if len(heads) > 2:
1945 if len(heads) > 2:
1985 raise util.Abort(_('repo has %d heads - '
1946 raise util.Abort(_('repo has %d heads - '
1986 'please merge with an explicit rev') %
1947 'please merge with an explicit rev') %
1987 len(heads))
1948 len(heads))
1988 if len(heads) == 1:
1949 if len(heads) == 1:
1989 raise util.Abort(_('there is nothing to merge - '
1950 raise util.Abort(_('there is nothing to merge - '
1990 'use "hg update" instead'))
1951 'use "hg update" instead'))
1991 parent = repo.dirstate.parents()[0]
1952 parent = repo.dirstate.parents()[0]
1992 if parent not in heads:
1953 if parent not in heads:
1993 raise util.Abort(_('working dir not at a head rev - '
1954 raise util.Abort(_('working dir not at a head rev - '
1994 'use "hg update" or merge with an explicit rev'))
1955 'use "hg update" or merge with an explicit rev'))
1995 node = parent == heads[0] and heads[-1] or heads[0]
1956 node = parent == heads[0] and heads[-1] or heads[0]
1996 return hg.merge(repo, node, force=force)
1957 return hg.merge(repo, node, force=force)
1997
1958
1998 def outgoing(ui, repo, dest=None, **opts):
1959 def outgoing(ui, repo, dest=None, **opts):
1999 """show changesets not found in destination
1960 """show changesets not found in destination
2000
1961
2001 Show changesets not found in the specified destination repository or
1962 Show changesets not found in the specified destination repository or
2002 the default push location. These are the changesets that would be pushed
1963 the default push location. These are the changesets that would be pushed
2003 if a push was requested.
1964 if a push was requested.
2004
1965
2005 See pull for valid destination format details.
1966 See pull for valid destination format details.
2006 """
1967 """
2007 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1968 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2008 setremoteconfig(ui, opts)
1969 setremoteconfig(ui, opts)
2009 revs = None
1970 revs = None
2010 if opts['rev']:
1971 if opts['rev']:
2011 revs = [repo.lookup(rev) for rev in opts['rev']]
1972 revs = [repo.lookup(rev) for rev in opts['rev']]
2012
1973
2013 other = hg.repository(ui, dest)
1974 other = hg.repository(ui, dest)
2014 o = repo.findoutgoing(other, force=opts['force'])
1975 o = repo.findoutgoing(other, force=opts['force'])
2015 if not o:
1976 if not o:
2016 ui.status(_("no changes found\n"))
1977 ui.status(_("no changes found\n"))
2017 return
1978 return
2018 o = repo.changelog.nodesbetween(o, revs)[0]
1979 o = repo.changelog.nodesbetween(o, revs)[0]
2019 if opts['newest_first']:
1980 if opts['newest_first']:
2020 o.reverse()
1981 o.reverse()
2021 displayer = show_changeset(ui, repo, opts)
1982 displayer = show_changeset(ui, repo, opts)
2022 for n in o:
1983 for n in o:
2023 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1984 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2024 if opts['no_merges'] and len(parents) == 2:
1985 if opts['no_merges'] and len(parents) == 2:
2025 continue
1986 continue
2026 displayer.show(changenode=n)
1987 displayer.show(changenode=n)
2027 if opts['patch']:
1988 if opts['patch']:
2028 prev = (parents and parents[0]) or nullid
1989 prev = (parents and parents[0]) or nullid
2029 patch.diff(repo, prev, n)
1990 patch.diff(repo, prev, n)
2030 ui.write("\n")
1991 ui.write("\n")
2031
1992
2032 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
1993 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2033 """show the parents of the working dir or revision
1994 """show the parents of the working dir or revision
2034
1995
2035 Print the working directory's parent revisions.
1996 Print the working directory's parent revisions.
2036 """
1997 """
2037 # legacy
1998 # legacy
2038 if file_ and not rev:
1999 if file_ and not rev:
2039 try:
2000 try:
2040 rev = repo.lookup(file_)
2001 rev = repo.lookup(file_)
2041 file_ = None
2002 file_ = None
2042 except hg.RepoError:
2003 except hg.RepoError:
2043 pass
2004 pass
2044 else:
2005 else:
2045 ui.warn(_("'hg parent REV' is deprecated, "
2006 ui.warn(_("'hg parent REV' is deprecated, "
2046 "please use 'hg parents -r REV instead\n"))
2007 "please use 'hg parents -r REV instead\n"))
2047
2008
2048 if rev:
2009 if rev:
2049 if file_:
2010 if file_:
2050 ctx = repo.filectx(file_, changeid=rev)
2011 ctx = repo.filectx(file_, changeid=rev)
2051 else:
2012 else:
2052 ctx = repo.changectx(rev)
2013 ctx = repo.changectx(rev)
2053 p = [cp.node() for cp in ctx.parents()]
2014 p = [cp.node() for cp in ctx.parents()]
2054 else:
2015 else:
2055 p = repo.dirstate.parents()
2016 p = repo.dirstate.parents()
2056
2017
2057 br = None
2018 br = None
2058 if branches is not None:
2019 if branches is not None:
2059 br = repo.branchlookup(p)
2020 br = repo.branchlookup(p)
2060 displayer = show_changeset(ui, repo, opts)
2021 displayer = show_changeset(ui, repo, opts)
2061 for n in p:
2022 for n in p:
2062 if n != nullid:
2023 if n != nullid:
2063 displayer.show(changenode=n, brinfo=br)
2024 displayer.show(changenode=n, brinfo=br)
2064
2025
2065 def paths(ui, repo, search=None):
2026 def paths(ui, repo, search=None):
2066 """show definition of symbolic path names
2027 """show definition of symbolic path names
2067
2028
2068 Show definition of symbolic path name NAME. If no name is given, show
2029 Show definition of symbolic path name NAME. If no name is given, show
2069 definition of available names.
2030 definition of available names.
2070
2031
2071 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2032 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2072 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2033 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2073 """
2034 """
2074 if search:
2035 if search:
2075 for name, path in ui.configitems("paths"):
2036 for name, path in ui.configitems("paths"):
2076 if name == search:
2037 if name == search:
2077 ui.write("%s\n" % path)
2038 ui.write("%s\n" % path)
2078 return
2039 return
2079 ui.warn(_("not found!\n"))
2040 ui.warn(_("not found!\n"))
2080 return 1
2041 return 1
2081 else:
2042 else:
2082 for name, path in ui.configitems("paths"):
2043 for name, path in ui.configitems("paths"):
2083 ui.write("%s = %s\n" % (name, path))
2044 ui.write("%s = %s\n" % (name, path))
2084
2045
2085 def postincoming(ui, repo, modheads, optupdate):
2046 def postincoming(ui, repo, modheads, optupdate):
2086 if modheads == 0:
2047 if modheads == 0:
2087 return
2048 return
2088 if optupdate:
2049 if optupdate:
2089 if modheads == 1:
2050 if modheads == 1:
2090 return hg.update(repo, repo.changelog.tip()) # update
2051 return hg.update(repo, repo.changelog.tip()) # update
2091 else:
2052 else:
2092 ui.status(_("not updating, since new heads added\n"))
2053 ui.status(_("not updating, since new heads added\n"))
2093 if modheads > 1:
2054 if modheads > 1:
2094 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2055 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2095 else:
2056 else:
2096 ui.status(_("(run 'hg update' to get a working copy)\n"))
2057 ui.status(_("(run 'hg update' to get a working copy)\n"))
2097
2058
2098 def pull(ui, repo, source="default", **opts):
2059 def pull(ui, repo, source="default", **opts):
2099 """pull changes from the specified source
2060 """pull changes from the specified source
2100
2061
2101 Pull changes from a remote repository to a local one.
2062 Pull changes from a remote repository to a local one.
2102
2063
2103 This finds all changes from the repository at the specified path
2064 This finds all changes from the repository at the specified path
2104 or URL and adds them to the local repository. By default, this
2065 or URL and adds them to the local repository. By default, this
2105 does not update the copy of the project in the working directory.
2066 does not update the copy of the project in the working directory.
2106
2067
2107 Valid URLs are of the form:
2068 Valid URLs are of the form:
2108
2069
2109 local/filesystem/path
2070 local/filesystem/path
2110 http://[user@]host[:port]/[path]
2071 http://[user@]host[:port]/[path]
2111 https://[user@]host[:port]/[path]
2072 https://[user@]host[:port]/[path]
2112 ssh://[user@]host[:port]/[path]
2073 ssh://[user@]host[:port]/[path]
2113
2074
2114 Some notes about using SSH with Mercurial:
2075 Some notes about using SSH with Mercurial:
2115 - SSH requires an accessible shell account on the destination machine
2076 - SSH requires an accessible shell account on the destination machine
2116 and a copy of hg in the remote path or specified with as remotecmd.
2077 and a copy of hg in the remote path or specified with as remotecmd.
2117 - path is relative to the remote user's home directory by default.
2078 - path is relative to the remote user's home directory by default.
2118 Use an extra slash at the start of a path to specify an absolute path:
2079 Use an extra slash at the start of a path to specify an absolute path:
2119 ssh://example.com//tmp/repository
2080 ssh://example.com//tmp/repository
2120 - Mercurial doesn't use its own compression via SSH; the right thing
2081 - Mercurial doesn't use its own compression via SSH; the right thing
2121 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2082 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2122 Host *.mylocalnetwork.example.com
2083 Host *.mylocalnetwork.example.com
2123 Compression off
2084 Compression off
2124 Host *
2085 Host *
2125 Compression on
2086 Compression on
2126 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2087 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2127 with the --ssh command line option.
2088 with the --ssh command line option.
2128 """
2089 """
2129 source = ui.expandpath(source)
2090 source = ui.expandpath(source)
2130 setremoteconfig(ui, opts)
2091 setremoteconfig(ui, opts)
2131
2092
2132 other = hg.repository(ui, source)
2093 other = hg.repository(ui, source)
2133 ui.status(_('pulling from %s\n') % (source))
2094 ui.status(_('pulling from %s\n') % (source))
2134 revs = None
2095 revs = None
2135 if opts['rev'] and not other.local():
2096 if opts['rev'] and not other.local():
2136 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2097 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2137 elif opts['rev']:
2098 elif opts['rev']:
2138 revs = [other.lookup(rev) for rev in opts['rev']]
2099 revs = [other.lookup(rev) for rev in opts['rev']]
2139 modheads = repo.pull(other, heads=revs, force=opts['force'])
2100 modheads = repo.pull(other, heads=revs, force=opts['force'])
2140 return postincoming(ui, repo, modheads, opts['update'])
2101 return postincoming(ui, repo, modheads, opts['update'])
2141
2102
2142 def push(ui, repo, dest=None, **opts):
2103 def push(ui, repo, dest=None, **opts):
2143 """push changes to the specified destination
2104 """push changes to the specified destination
2144
2105
2145 Push changes from the local repository to the given destination.
2106 Push changes from the local repository to the given destination.
2146
2107
2147 This is the symmetrical operation for pull. It helps to move
2108 This is the symmetrical operation for pull. It helps to move
2148 changes from the current repository to a different one. If the
2109 changes from the current repository to a different one. If the
2149 destination is local this is identical to a pull in that directory
2110 destination is local this is identical to a pull in that directory
2150 from the current one.
2111 from the current one.
2151
2112
2152 By default, push will refuse to run if it detects the result would
2113 By default, push will refuse to run if it detects the result would
2153 increase the number of remote heads. This generally indicates the
2114 increase the number of remote heads. This generally indicates the
2154 the client has forgotten to sync and merge before pushing.
2115 the client has forgotten to sync and merge before pushing.
2155
2116
2156 Valid URLs are of the form:
2117 Valid URLs are of the form:
2157
2118
2158 local/filesystem/path
2119 local/filesystem/path
2159 ssh://[user@]host[:port]/[path]
2120 ssh://[user@]host[:port]/[path]
2160
2121
2161 Look at the help text for the pull command for important details
2122 Look at the help text for the pull command for important details
2162 about ssh:// URLs.
2123 about ssh:// URLs.
2163
2124
2164 Pushing to http:// and https:// URLs is possible, too, if this
2125 Pushing to http:// and https:// URLs is possible, too, if this
2165 feature is enabled on the remote Mercurial server.
2126 feature is enabled on the remote Mercurial server.
2166 """
2127 """
2167 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2128 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2168 setremoteconfig(ui, opts)
2129 setremoteconfig(ui, opts)
2169
2130
2170 other = hg.repository(ui, dest)
2131 other = hg.repository(ui, dest)
2171 ui.status('pushing to %s\n' % (dest))
2132 ui.status('pushing to %s\n' % (dest))
2172 revs = None
2133 revs = None
2173 if opts['rev']:
2134 if opts['rev']:
2174 revs = [repo.lookup(rev) for rev in opts['rev']]
2135 revs = [repo.lookup(rev) for rev in opts['rev']]
2175 r = repo.push(other, opts['force'], revs=revs)
2136 r = repo.push(other, opts['force'], revs=revs)
2176 return r == 0
2137 return r == 0
2177
2138
2178 def rawcommit(ui, repo, *flist, **rc):
2139 def rawcommit(ui, repo, *flist, **rc):
2179 """raw commit interface (DEPRECATED)
2140 """raw commit interface (DEPRECATED)
2180
2141
2181 (DEPRECATED)
2142 (DEPRECATED)
2182 Lowlevel commit, for use in helper scripts.
2143 Lowlevel commit, for use in helper scripts.
2183
2144
2184 This command is not intended to be used by normal users, as it is
2145 This command is not intended to be used by normal users, as it is
2185 primarily useful for importing from other SCMs.
2146 primarily useful for importing from other SCMs.
2186
2147
2187 This command is now deprecated and will be removed in a future
2148 This command is now deprecated and will be removed in a future
2188 release, please use debugsetparents and commit instead.
2149 release, please use debugsetparents and commit instead.
2189 """
2150 """
2190
2151
2191 ui.warn(_("(the rawcommit command is deprecated)\n"))
2152 ui.warn(_("(the rawcommit command is deprecated)\n"))
2192
2153
2193 message = rc['message']
2154 message = rc['message']
2194 if not message and rc['logfile']:
2155 if not message and rc['logfile']:
2195 try:
2156 try:
2196 message = open(rc['logfile']).read()
2157 message = open(rc['logfile']).read()
2197 except IOError:
2158 except IOError:
2198 pass
2159 pass
2199 if not message and not rc['logfile']:
2160 if not message and not rc['logfile']:
2200 raise util.Abort(_("missing commit message"))
2161 raise util.Abort(_("missing commit message"))
2201
2162
2202 files = relpath(repo, list(flist))
2163 files = relpath(repo, list(flist))
2203 if rc['files']:
2164 if rc['files']:
2204 files += open(rc['files']).read().splitlines()
2165 files += open(rc['files']).read().splitlines()
2205
2166
2206 rc['parent'] = map(repo.lookup, rc['parent'])
2167 rc['parent'] = map(repo.lookup, rc['parent'])
2207
2168
2208 try:
2169 try:
2209 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2170 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2210 except ValueError, inst:
2171 except ValueError, inst:
2211 raise util.Abort(str(inst))
2172 raise util.Abort(str(inst))
2212
2173
2213 def recover(ui, repo):
2174 def recover(ui, repo):
2214 """roll back an interrupted transaction
2175 """roll back an interrupted transaction
2215
2176
2216 Recover from an interrupted commit or pull.
2177 Recover from an interrupted commit or pull.
2217
2178
2218 This command tries to fix the repository status after an interrupted
2179 This command tries to fix the repository status after an interrupted
2219 operation. It should only be necessary when Mercurial suggests it.
2180 operation. It should only be necessary when Mercurial suggests it.
2220 """
2181 """
2221 if repo.recover():
2182 if repo.recover():
2222 return hg.verify(repo)
2183 return hg.verify(repo)
2223 return 1
2184 return 1
2224
2185
2225 def remove(ui, repo, *pats, **opts):
2186 def remove(ui, repo, *pats, **opts):
2226 """remove the specified files on the next commit
2187 """remove the specified files on the next commit
2227
2188
2228 Schedule the indicated files for removal from the repository.
2189 Schedule the indicated files for removal from the repository.
2229
2190
2230 This command schedules the files to be removed at the next commit.
2191 This command schedules the files to be removed at the next commit.
2231 This only removes files from the current branch, not from the
2192 This only removes files from the current branch, not from the
2232 entire project history. If the files still exist in the working
2193 entire project history. If the files still exist in the working
2233 directory, they will be deleted from it. If invoked with --after,
2194 directory, they will be deleted from it. If invoked with --after,
2234 files that have been manually deleted are marked as removed.
2195 files that have been manually deleted are marked as removed.
2235
2196
2236 Modified files and added files are not removed by default. To
2197 Modified files and added files are not removed by default. To
2237 remove them, use the -f/--force option.
2198 remove them, use the -f/--force option.
2238 """
2199 """
2239 names = []
2200 names = []
2240 if not opts['after'] and not pats:
2201 if not opts['after'] and not pats:
2241 raise util.Abort(_('no files specified'))
2202 raise util.Abort(_('no files specified'))
2242 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2203 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2243 exact = dict.fromkeys(files)
2204 exact = dict.fromkeys(files)
2244 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2205 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2245 modified, added, removed, deleted, unknown = mardu
2206 modified, added, removed, deleted, unknown = mardu
2246 remove, forget = [], []
2207 remove, forget = [], []
2247 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2208 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2248 reason = None
2209 reason = None
2249 if abs not in deleted and opts['after']:
2210 if abs not in deleted and opts['after']:
2250 reason = _('is still present')
2211 reason = _('is still present')
2251 elif abs in modified and not opts['force']:
2212 elif abs in modified and not opts['force']:
2252 reason = _('is modified (use -f to force removal)')
2213 reason = _('is modified (use -f to force removal)')
2253 elif abs in added:
2214 elif abs in added:
2254 if opts['force']:
2215 if opts['force']:
2255 forget.append(abs)
2216 forget.append(abs)
2256 continue
2217 continue
2257 reason = _('has been marked for add (use -f to force removal)')
2218 reason = _('has been marked for add (use -f to force removal)')
2258 elif abs in unknown:
2219 elif abs in unknown:
2259 reason = _('is not managed')
2220 reason = _('is not managed')
2260 elif abs in removed:
2221 elif abs in removed:
2261 continue
2222 continue
2262 if reason:
2223 if reason:
2263 if exact:
2224 if exact:
2264 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2225 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2265 else:
2226 else:
2266 if ui.verbose or not exact:
2227 if ui.verbose or not exact:
2267 ui.status(_('removing %s\n') % rel)
2228 ui.status(_('removing %s\n') % rel)
2268 remove.append(abs)
2229 remove.append(abs)
2269 repo.forget(forget)
2230 repo.forget(forget)
2270 repo.remove(remove, unlink=not opts['after'])
2231 repo.remove(remove, unlink=not opts['after'])
2271
2232
2272 def rename(ui, repo, *pats, **opts):
2233 def rename(ui, repo, *pats, **opts):
2273 """rename files; equivalent of copy + remove
2234 """rename files; equivalent of copy + remove
2274
2235
2275 Mark dest as copies of sources; mark sources for deletion. If
2236 Mark dest as copies of sources; mark sources for deletion. If
2276 dest is a directory, copies are put in that directory. If dest is
2237 dest is a directory, copies are put in that directory. If dest is
2277 a file, there can only be one source.
2238 a file, there can only be one source.
2278
2239
2279 By default, this command copies the contents of files as they
2240 By default, this command copies the contents of files as they
2280 stand in the working directory. If invoked with --after, the
2241 stand in the working directory. If invoked with --after, the
2281 operation is recorded, but no copying is performed.
2242 operation is recorded, but no copying is performed.
2282
2243
2283 This command takes effect in the next commit.
2244 This command takes effect in the next commit.
2284
2245
2285 NOTE: This command should be treated as experimental. While it
2246 NOTE: This command should be treated as experimental. While it
2286 should properly record rename files, this information is not yet
2247 should properly record rename files, this information is not yet
2287 fully used by merge, nor fully reported by log.
2248 fully used by merge, nor fully reported by log.
2288 """
2249 """
2289 wlock = repo.wlock(0)
2250 wlock = repo.wlock(0)
2290 errs, copied = docopy(ui, repo, pats, opts, wlock)
2251 errs, copied = docopy(ui, repo, pats, opts, wlock)
2291 names = []
2252 names = []
2292 for abs, rel, exact in copied:
2253 for abs, rel, exact in copied:
2293 if ui.verbose or not exact:
2254 if ui.verbose or not exact:
2294 ui.status(_('removing %s\n') % rel)
2255 ui.status(_('removing %s\n') % rel)
2295 names.append(abs)
2256 names.append(abs)
2296 if not opts.get('dry_run'):
2257 if not opts.get('dry_run'):
2297 repo.remove(names, True, wlock)
2258 repo.remove(names, True, wlock)
2298 return errs
2259 return errs
2299
2260
2300 def revert(ui, repo, *pats, **opts):
2261 def revert(ui, repo, *pats, **opts):
2301 """revert files or dirs to their states as of some revision
2262 """revert files or dirs to their states as of some revision
2302
2263
2303 With no revision specified, revert the named files or directories
2264 With no revision specified, revert the named files or directories
2304 to the contents they had in the parent of the working directory.
2265 to the contents they had in the parent of the working directory.
2305 This restores the contents of the affected files to an unmodified
2266 This restores the contents of the affected files to an unmodified
2306 state. If the working directory has two parents, you must
2267 state. If the working directory has two parents, you must
2307 explicitly specify the revision to revert to.
2268 explicitly specify the revision to revert to.
2308
2269
2309 Modified files are saved with a .orig suffix before reverting.
2270 Modified files are saved with a .orig suffix before reverting.
2310 To disable these backups, use --no-backup.
2271 To disable these backups, use --no-backup.
2311
2272
2312 Using the -r option, revert the given files or directories to
2273 Using the -r option, revert the given files or directories to
2313 their contents as of a specific revision. This can be helpful to"roll
2274 their contents as of a specific revision. This can be helpful to"roll
2314 back" some or all of a change that should not have been committed.
2275 back" some or all of a change that should not have been committed.
2315
2276
2316 Revert modifies the working directory. It does not commit any
2277 Revert modifies the working directory. It does not commit any
2317 changes, or change the parent of the working directory. If you
2278 changes, or change the parent of the working directory. If you
2318 revert to a revision other than the parent of the working
2279 revert to a revision other than the parent of the working
2319 directory, the reverted files will thus appear modified
2280 directory, the reverted files will thus appear modified
2320 afterwards.
2281 afterwards.
2321
2282
2322 If a file has been deleted, it is recreated. If the executable
2283 If a file has been deleted, it is recreated. If the executable
2323 mode of a file was changed, it is reset.
2284 mode of a file was changed, it is reset.
2324
2285
2325 If names are given, all files matching the names are reverted.
2286 If names are given, all files matching the names are reverted.
2326
2287
2327 If no arguments are given, all files in the repository are reverted.
2288 If no arguments are given, all files in the repository are reverted.
2328 """
2289 """
2329 parent, p2 = repo.dirstate.parents()
2290 parent, p2 = repo.dirstate.parents()
2330 if opts['rev']:
2291 if opts['rev']:
2331 node = repo.lookup(opts['rev'])
2292 node = repo.lookup(opts['rev'])
2332 elif p2 != nullid:
2293 elif p2 != nullid:
2333 raise util.Abort(_('working dir has two parents; '
2294 raise util.Abort(_('working dir has two parents; '
2334 'you must specify the revision to revert to'))
2295 'you must specify the revision to revert to'))
2335 else:
2296 else:
2336 node = parent
2297 node = parent
2337 mf = repo.manifest.read(repo.changelog.read(node)[0])
2298 mf = repo.manifest.read(repo.changelog.read(node)[0])
2338 if node == parent:
2299 if node == parent:
2339 pmf = mf
2300 pmf = mf
2340 else:
2301 else:
2341 pmf = None
2302 pmf = None
2342
2303
2343 wlock = repo.wlock()
2304 wlock = repo.wlock()
2344
2305
2345 # need all matching names in dirstate and manifest of target rev,
2306 # need all matching names in dirstate and manifest of target rev,
2346 # so have to walk both. do not print errors if files exist in one
2307 # so have to walk both. do not print errors if files exist in one
2347 # but not other.
2308 # but not other.
2348
2309
2349 names = {}
2310 names = {}
2350 target_only = {}
2311 target_only = {}
2351
2312
2352 # walk dirstate.
2313 # walk dirstate.
2353
2314
2354 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2315 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2355 badmatch=mf.has_key):
2316 badmatch=mf.has_key):
2356 names[abs] = (rel, exact)
2317 names[abs] = (rel, exact)
2357 if src == 'b':
2318 if src == 'b':
2358 target_only[abs] = True
2319 target_only[abs] = True
2359
2320
2360 # walk target manifest.
2321 # walk target manifest.
2361
2322
2362 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2323 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2363 badmatch=names.has_key):
2324 badmatch=names.has_key):
2364 if abs in names: continue
2325 if abs in names: continue
2365 names[abs] = (rel, exact)
2326 names[abs] = (rel, exact)
2366 target_only[abs] = True
2327 target_only[abs] = True
2367
2328
2368 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2329 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2369 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2330 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2370
2331
2371 revert = ([], _('reverting %s\n'))
2332 revert = ([], _('reverting %s\n'))
2372 add = ([], _('adding %s\n'))
2333 add = ([], _('adding %s\n'))
2373 remove = ([], _('removing %s\n'))
2334 remove = ([], _('removing %s\n'))
2374 forget = ([], _('forgetting %s\n'))
2335 forget = ([], _('forgetting %s\n'))
2375 undelete = ([], _('undeleting %s\n'))
2336 undelete = ([], _('undeleting %s\n'))
2376 update = {}
2337 update = {}
2377
2338
2378 disptable = (
2339 disptable = (
2379 # dispatch table:
2340 # dispatch table:
2380 # file state
2341 # file state
2381 # action if in target manifest
2342 # action if in target manifest
2382 # action if not in target manifest
2343 # action if not in target manifest
2383 # make backup if in target manifest
2344 # make backup if in target manifest
2384 # make backup if not in target manifest
2345 # make backup if not in target manifest
2385 (modified, revert, remove, True, True),
2346 (modified, revert, remove, True, True),
2386 (added, revert, forget, True, False),
2347 (added, revert, forget, True, False),
2387 (removed, undelete, None, False, False),
2348 (removed, undelete, None, False, False),
2388 (deleted, revert, remove, False, False),
2349 (deleted, revert, remove, False, False),
2389 (unknown, add, None, True, False),
2350 (unknown, add, None, True, False),
2390 (target_only, add, None, False, False),
2351 (target_only, add, None, False, False),
2391 )
2352 )
2392
2353
2393 entries = names.items()
2354 entries = names.items()
2394 entries.sort()
2355 entries.sort()
2395
2356
2396 for abs, (rel, exact) in entries:
2357 for abs, (rel, exact) in entries:
2397 mfentry = mf.get(abs)
2358 mfentry = mf.get(abs)
2398 def handle(xlist, dobackup):
2359 def handle(xlist, dobackup):
2399 xlist[0].append(abs)
2360 xlist[0].append(abs)
2400 update[abs] = 1
2361 update[abs] = 1
2401 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2362 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2402 bakname = "%s.orig" % rel
2363 bakname = "%s.orig" % rel
2403 ui.note(_('saving current version of %s as %s\n') %
2364 ui.note(_('saving current version of %s as %s\n') %
2404 (rel, bakname))
2365 (rel, bakname))
2405 if not opts.get('dry_run'):
2366 if not opts.get('dry_run'):
2406 shutil.copyfile(rel, bakname)
2367 shutil.copyfile(rel, bakname)
2407 shutil.copymode(rel, bakname)
2368 shutil.copymode(rel, bakname)
2408 if ui.verbose or not exact:
2369 if ui.verbose or not exact:
2409 ui.status(xlist[1] % rel)
2370 ui.status(xlist[1] % rel)
2410 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2371 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2411 if abs not in table: continue
2372 if abs not in table: continue
2412 # file has changed in dirstate
2373 # file has changed in dirstate
2413 if mfentry:
2374 if mfentry:
2414 handle(hitlist, backuphit)
2375 handle(hitlist, backuphit)
2415 elif misslist is not None:
2376 elif misslist is not None:
2416 handle(misslist, backupmiss)
2377 handle(misslist, backupmiss)
2417 else:
2378 else:
2418 if exact: ui.warn(_('file not managed: %s\n' % rel))
2379 if exact: ui.warn(_('file not managed: %s\n' % rel))
2419 break
2380 break
2420 else:
2381 else:
2421 # file has not changed in dirstate
2382 # file has not changed in dirstate
2422 if node == parent:
2383 if node == parent:
2423 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2384 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2424 continue
2385 continue
2425 if pmf is None:
2386 if pmf is None:
2426 # only need parent manifest in this unlikely case,
2387 # only need parent manifest in this unlikely case,
2427 # so do not read by default
2388 # so do not read by default
2428 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2389 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2429 if abs in pmf:
2390 if abs in pmf:
2430 if mfentry:
2391 if mfentry:
2431 # if version of file is same in parent and target
2392 # if version of file is same in parent and target
2432 # manifests, do nothing
2393 # manifests, do nothing
2433 if pmf[abs] != mfentry:
2394 if pmf[abs] != mfentry:
2434 handle(revert, False)
2395 handle(revert, False)
2435 else:
2396 else:
2436 handle(remove, False)
2397 handle(remove, False)
2437
2398
2438 if not opts.get('dry_run'):
2399 if not opts.get('dry_run'):
2439 repo.dirstate.forget(forget[0])
2400 repo.dirstate.forget(forget[0])
2440 r = hg.revert(repo, node, update.has_key, wlock)
2401 r = hg.revert(repo, node, update.has_key, wlock)
2441 repo.dirstate.update(add[0], 'a')
2402 repo.dirstate.update(add[0], 'a')
2442 repo.dirstate.update(undelete[0], 'n')
2403 repo.dirstate.update(undelete[0], 'n')
2443 repo.dirstate.update(remove[0], 'r')
2404 repo.dirstate.update(remove[0], 'r')
2444 return r
2405 return r
2445
2406
2446 def rollback(ui, repo):
2407 def rollback(ui, repo):
2447 """roll back the last transaction in this repository
2408 """roll back the last transaction in this repository
2448
2409
2449 Roll back the last transaction in this repository, restoring the
2410 Roll back the last transaction in this repository, restoring the
2450 project to its state prior to the transaction.
2411 project to its state prior to the transaction.
2451
2412
2452 Transactions are used to encapsulate the effects of all commands
2413 Transactions are used to encapsulate the effects of all commands
2453 that create new changesets or propagate existing changesets into a
2414 that create new changesets or propagate existing changesets into a
2454 repository. For example, the following commands are transactional,
2415 repository. For example, the following commands are transactional,
2455 and their effects can be rolled back:
2416 and their effects can be rolled back:
2456
2417
2457 commit
2418 commit
2458 import
2419 import
2459 pull
2420 pull
2460 push (with this repository as destination)
2421 push (with this repository as destination)
2461 unbundle
2422 unbundle
2462
2423
2463 This command should be used with care. There is only one level of
2424 This command should be used with care. There is only one level of
2464 rollback, and there is no way to undo a rollback.
2425 rollback, and there is no way to undo a rollback.
2465
2426
2466 This command is not intended for use on public repositories. Once
2427 This command is not intended for use on public repositories. Once
2467 changes are visible for pull by other users, rolling a transaction
2428 changes are visible for pull by other users, rolling a transaction
2468 back locally is ineffective (someone else may already have pulled
2429 back locally is ineffective (someone else may already have pulled
2469 the changes). Furthermore, a race is possible with readers of the
2430 the changes). Furthermore, a race is possible with readers of the
2470 repository; for example an in-progress pull from the repository
2431 repository; for example an in-progress pull from the repository
2471 may fail if a rollback is performed.
2432 may fail if a rollback is performed.
2472 """
2433 """
2473 repo.rollback()
2434 repo.rollback()
2474
2435
2475 def root(ui, repo):
2436 def root(ui, repo):
2476 """print the root (top) of the current working dir
2437 """print the root (top) of the current working dir
2477
2438
2478 Print the root directory of the current repository.
2439 Print the root directory of the current repository.
2479 """
2440 """
2480 ui.write(repo.root + "\n")
2441 ui.write(repo.root + "\n")
2481
2442
2482 def serve(ui, repo, **opts):
2443 def serve(ui, repo, **opts):
2483 """export the repository via HTTP
2444 """export the repository via HTTP
2484
2445
2485 Start a local HTTP repository browser and pull server.
2446 Start a local HTTP repository browser and pull server.
2486
2447
2487 By default, the server logs accesses to stdout and errors to
2448 By default, the server logs accesses to stdout and errors to
2488 stderr. Use the "-A" and "-E" options to log to files.
2449 stderr. Use the "-A" and "-E" options to log to files.
2489 """
2450 """
2490
2451
2491 if opts["stdio"]:
2452 if opts["stdio"]:
2492 if repo is None:
2453 if repo is None:
2493 raise hg.RepoError(_('no repo found'))
2454 raise hg.RepoError(_('no repo found'))
2494 s = sshserver.sshserver(ui, repo)
2455 s = sshserver.sshserver(ui, repo)
2495 s.serve_forever()
2456 s.serve_forever()
2496
2457
2497 optlist = ("name templates style address port ipv6"
2458 optlist = ("name templates style address port ipv6"
2498 " accesslog errorlog webdir_conf")
2459 " accesslog errorlog webdir_conf")
2499 for o in optlist.split():
2460 for o in optlist.split():
2500 if opts[o]:
2461 if opts[o]:
2501 ui.setconfig("web", o, opts[o])
2462 ui.setconfig("web", o, opts[o])
2502
2463
2503 if repo is None and not ui.config("web", "webdir_conf"):
2464 if repo is None and not ui.config("web", "webdir_conf"):
2504 raise hg.RepoError(_('no repo found'))
2465 raise hg.RepoError(_('no repo found'))
2505
2466
2506 if opts['daemon'] and not opts['daemon_pipefds']:
2467 if opts['daemon'] and not opts['daemon_pipefds']:
2507 rfd, wfd = os.pipe()
2468 rfd, wfd = os.pipe()
2508 args = sys.argv[:]
2469 args = sys.argv[:]
2509 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2470 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2510 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2471 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2511 args[0], args)
2472 args[0], args)
2512 os.close(wfd)
2473 os.close(wfd)
2513 os.read(rfd, 1)
2474 os.read(rfd, 1)
2514 os._exit(0)
2475 os._exit(0)
2515
2476
2516 try:
2477 try:
2517 httpd = hgweb.server.create_server(ui, repo)
2478 httpd = hgweb.server.create_server(ui, repo)
2518 except socket.error, inst:
2479 except socket.error, inst:
2519 raise util.Abort(_('cannot start server: ') + inst.args[1])
2480 raise util.Abort(_('cannot start server: ') + inst.args[1])
2520
2481
2521 if ui.verbose:
2482 if ui.verbose:
2522 addr, port = httpd.socket.getsockname()
2483 addr, port = httpd.socket.getsockname()
2523 if addr == '0.0.0.0':
2484 if addr == '0.0.0.0':
2524 addr = socket.gethostname()
2485 addr = socket.gethostname()
2525 else:
2486 else:
2526 try:
2487 try:
2527 addr = socket.gethostbyaddr(addr)[0]
2488 addr = socket.gethostbyaddr(addr)[0]
2528 except socket.error:
2489 except socket.error:
2529 pass
2490 pass
2530 if port != 80:
2491 if port != 80:
2531 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2492 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2532 else:
2493 else:
2533 ui.status(_('listening at http://%s/\n') % addr)
2494 ui.status(_('listening at http://%s/\n') % addr)
2534
2495
2535 if opts['pid_file']:
2496 if opts['pid_file']:
2536 fp = open(opts['pid_file'], 'w')
2497 fp = open(opts['pid_file'], 'w')
2537 fp.write(str(os.getpid()) + '\n')
2498 fp.write(str(os.getpid()) + '\n')
2538 fp.close()
2499 fp.close()
2539
2500
2540 if opts['daemon_pipefds']:
2501 if opts['daemon_pipefds']:
2541 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2502 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2542 os.close(rfd)
2503 os.close(rfd)
2543 os.write(wfd, 'y')
2504 os.write(wfd, 'y')
2544 os.close(wfd)
2505 os.close(wfd)
2545 sys.stdout.flush()
2506 sys.stdout.flush()
2546 sys.stderr.flush()
2507 sys.stderr.flush()
2547 fd = os.open(util.nulldev, os.O_RDWR)
2508 fd = os.open(util.nulldev, os.O_RDWR)
2548 if fd != 0: os.dup2(fd, 0)
2509 if fd != 0: os.dup2(fd, 0)
2549 if fd != 1: os.dup2(fd, 1)
2510 if fd != 1: os.dup2(fd, 1)
2550 if fd != 2: os.dup2(fd, 2)
2511 if fd != 2: os.dup2(fd, 2)
2551 if fd not in (0, 1, 2): os.close(fd)
2512 if fd not in (0, 1, 2): os.close(fd)
2552
2513
2553 httpd.serve_forever()
2514 httpd.serve_forever()
2554
2515
2555 def status(ui, repo, *pats, **opts):
2516 def status(ui, repo, *pats, **opts):
2556 """show changed files in the working directory
2517 """show changed files in the working directory
2557
2518
2558 Show status of files in the repository. If names are given, only
2519 Show status of files in the repository. If names are given, only
2559 files that match are shown. Files that are clean or ignored, are
2520 files that match are shown. Files that are clean or ignored, are
2560 not listed unless -c (clean), -i (ignored) or -A is given.
2521 not listed unless -c (clean), -i (ignored) or -A is given.
2561
2522
2562 The codes used to show the status of files are:
2523 The codes used to show the status of files are:
2563 M = modified
2524 M = modified
2564 A = added
2525 A = added
2565 R = removed
2526 R = removed
2566 C = clean
2527 C = clean
2567 ! = deleted, but still tracked
2528 ! = deleted, but still tracked
2568 ? = not tracked
2529 ? = not tracked
2569 I = ignored (not shown by default)
2530 I = ignored (not shown by default)
2570 = the previous added file was copied from here
2531 = the previous added file was copied from here
2571 """
2532 """
2572
2533
2573 all = opts['all']
2534 all = opts['all']
2574
2535
2575 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2536 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2576 cwd = (pats and repo.getcwd()) or ''
2537 cwd = (pats and repo.getcwd()) or ''
2577 modified, added, removed, deleted, unknown, ignored, clean = [
2538 modified, added, removed, deleted, unknown, ignored, clean = [
2578 [util.pathto(cwd, x) for x in n]
2539 [util.pathto(cwd, x) for x in n]
2579 for n in repo.status(files=files, match=matchfn,
2540 for n in repo.status(files=files, match=matchfn,
2580 list_ignored=all or opts['ignored'],
2541 list_ignored=all or opts['ignored'],
2581 list_clean=all or opts['clean'])]
2542 list_clean=all or opts['clean'])]
2582
2543
2583 changetypes = (('modified', 'M', modified),
2544 changetypes = (('modified', 'M', modified),
2584 ('added', 'A', added),
2545 ('added', 'A', added),
2585 ('removed', 'R', removed),
2546 ('removed', 'R', removed),
2586 ('deleted', '!', deleted),
2547 ('deleted', '!', deleted),
2587 ('unknown', '?', unknown),
2548 ('unknown', '?', unknown),
2588 ('ignored', 'I', ignored))
2549 ('ignored', 'I', ignored))
2589
2550
2590 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2551 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2591
2552
2592 end = opts['print0'] and '\0' or '\n'
2553 end = opts['print0'] and '\0' or '\n'
2593
2554
2594 for opt, char, changes in ([ct for ct in explicit_changetypes
2555 for opt, char, changes in ([ct for ct in explicit_changetypes
2595 if all or opts[ct[0]]]
2556 if all or opts[ct[0]]]
2596 or changetypes):
2557 or changetypes):
2597 if opts['no_status']:
2558 if opts['no_status']:
2598 format = "%%s%s" % end
2559 format = "%%s%s" % end
2599 else:
2560 else:
2600 format = "%s %%s%s" % (char, end)
2561 format = "%s %%s%s" % (char, end)
2601
2562
2602 for f in changes:
2563 for f in changes:
2603 ui.write(format % f)
2564 ui.write(format % f)
2604 if ((all or opts.get('copies')) and not opts.get('no_status')
2565 if ((all or opts.get('copies')) and not opts.get('no_status')
2605 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2566 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2606 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2567 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2607
2568
2608 def tag(ui, repo, name, rev_=None, **opts):
2569 def tag(ui, repo, name, rev_=None, **opts):
2609 """add a tag for the current tip or a given revision
2570 """add a tag for the current tip or a given revision
2610
2571
2611 Name a particular revision using <name>.
2572 Name a particular revision using <name>.
2612
2573
2613 Tags are used to name particular revisions of the repository and are
2574 Tags are used to name particular revisions of the repository and are
2614 very useful to compare different revision, to go back to significant
2575 very useful to compare different revision, to go back to significant
2615 earlier versions or to mark branch points as releases, etc.
2576 earlier versions or to mark branch points as releases, etc.
2616
2577
2617 If no revision is given, the parent of the working directory is used.
2578 If no revision is given, the parent of the working directory is used.
2618
2579
2619 To facilitate version control, distribution, and merging of tags,
2580 To facilitate version control, distribution, and merging of tags,
2620 they are stored as a file named ".hgtags" which is managed
2581 they are stored as a file named ".hgtags" which is managed
2621 similarly to other project files and can be hand-edited if
2582 similarly to other project files and can be hand-edited if
2622 necessary. The file '.hg/localtags' is used for local tags (not
2583 necessary. The file '.hg/localtags' is used for local tags (not
2623 shared among repositories).
2584 shared among repositories).
2624 """
2585 """
2625 if name in ['tip', '.']:
2586 if name in ['tip', '.']:
2626 raise util.Abort(_("the name '%s' is reserved") % name)
2587 raise util.Abort(_("the name '%s' is reserved") % name)
2627 if rev_ is not None:
2588 if rev_ is not None:
2628 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2589 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2629 "please use 'hg tag [-r REV] NAME' instead\n"))
2590 "please use 'hg tag [-r REV] NAME' instead\n"))
2630 if opts['rev']:
2591 if opts['rev']:
2631 raise util.Abort(_("use only one form to specify the revision"))
2592 raise util.Abort(_("use only one form to specify the revision"))
2632 if opts['rev']:
2593 if opts['rev']:
2633 rev_ = opts['rev']
2594 rev_ = opts['rev']
2634 if rev_:
2595 if rev_:
2635 r = hex(repo.lookup(rev_))
2596 r = hex(repo.lookup(rev_))
2636 else:
2597 else:
2637 p1, p2 = repo.dirstate.parents()
2598 p1, p2 = repo.dirstate.parents()
2638 if p1 == nullid:
2599 if p1 == nullid:
2639 raise util.Abort(_('no revision to tag'))
2600 raise util.Abort(_('no revision to tag'))
2640 if p2 != nullid:
2601 if p2 != nullid:
2641 raise util.Abort(_('outstanding uncommitted merges'))
2602 raise util.Abort(_('outstanding uncommitted merges'))
2642 r = hex(p1)
2603 r = hex(p1)
2643
2604
2644 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2605 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2645 opts['date'])
2606 opts['date'])
2646
2607
2647 def tags(ui, repo):
2608 def tags(ui, repo):
2648 """list repository tags
2609 """list repository tags
2649
2610
2650 List the repository tags.
2611 List the repository tags.
2651
2612
2652 This lists both regular and local tags.
2613 This lists both regular and local tags.
2653 """
2614 """
2654
2615
2655 l = repo.tagslist()
2616 l = repo.tagslist()
2656 l.reverse()
2617 l.reverse()
2657 for t, n in l:
2618 for t, n in l:
2658 try:
2619 try:
2659 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2620 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2660 except KeyError:
2621 except KeyError:
2661 r = " ?:?"
2622 r = " ?:?"
2662 if ui.quiet:
2623 if ui.quiet:
2663 ui.write("%s\n" % t)
2624 ui.write("%s\n" % t)
2664 else:
2625 else:
2665 ui.write("%-30s %s\n" % (t, r))
2626 ui.write("%-30s %s\n" % (t, r))
2666
2627
2667 def tip(ui, repo, **opts):
2628 def tip(ui, repo, **opts):
2668 """show the tip revision
2629 """show the tip revision
2669
2630
2670 Show the tip revision.
2631 Show the tip revision.
2671 """
2632 """
2672 n = repo.changelog.tip()
2633 n = repo.changelog.tip()
2673 br = None
2634 br = None
2674 if opts['branches']:
2635 if opts['branches']:
2675 br = repo.branchlookup([n])
2636 br = repo.branchlookup([n])
2676 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2637 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2677 if opts['patch']:
2638 if opts['patch']:
2678 patch.diff(repo, repo.changelog.parents(n)[0], n)
2639 patch.diff(repo, repo.changelog.parents(n)[0], n)
2679
2640
2680 def unbundle(ui, repo, fname, **opts):
2641 def unbundle(ui, repo, fname, **opts):
2681 """apply a changegroup file
2642 """apply a changegroup file
2682
2643
2683 Apply a compressed changegroup file generated by the bundle
2644 Apply a compressed changegroup file generated by the bundle
2684 command.
2645 command.
2685 """
2646 """
2686 f = urllib.urlopen(fname)
2647 f = urllib.urlopen(fname)
2687
2648
2688 header = f.read(6)
2649 header = f.read(6)
2689 if not header.startswith("HG"):
2650 if not header.startswith("HG"):
2690 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2651 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2691 elif not header.startswith("HG10"):
2652 elif not header.startswith("HG10"):
2692 raise util.Abort(_("%s: unknown bundle version") % fname)
2653 raise util.Abort(_("%s: unknown bundle version") % fname)
2693 elif header == "HG10BZ":
2654 elif header == "HG10BZ":
2694 def generator(f):
2655 def generator(f):
2695 zd = bz2.BZ2Decompressor()
2656 zd = bz2.BZ2Decompressor()
2696 zd.decompress("BZ")
2657 zd.decompress("BZ")
2697 for chunk in f:
2658 for chunk in f:
2698 yield zd.decompress(chunk)
2659 yield zd.decompress(chunk)
2699 elif header == "HG10UN":
2660 elif header == "HG10UN":
2700 def generator(f):
2661 def generator(f):
2701 for chunk in f:
2662 for chunk in f:
2702 yield chunk
2663 yield chunk
2703 else:
2664 else:
2704 raise util.Abort(_("%s: unknown bundle compression type")
2665 raise util.Abort(_("%s: unknown bundle compression type")
2705 % fname)
2666 % fname)
2706 gen = generator(util.filechunkiter(f, 4096))
2667 gen = generator(util.filechunkiter(f, 4096))
2707 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2668 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2708 'bundle:' + fname)
2669 'bundle:' + fname)
2709 return postincoming(ui, repo, modheads, opts['update'])
2670 return postincoming(ui, repo, modheads, opts['update'])
2710
2671
2711 def undo(ui, repo):
2672 def undo(ui, repo):
2712 """undo the last commit or pull (DEPRECATED)
2673 """undo the last commit or pull (DEPRECATED)
2713
2674
2714 (DEPRECATED)
2675 (DEPRECATED)
2715 This command is now deprecated and will be removed in a future
2676 This command is now deprecated and will be removed in a future
2716 release. Please use the rollback command instead. For usage
2677 release. Please use the rollback command instead. For usage
2717 instructions, see the rollback command.
2678 instructions, see the rollback command.
2718 """
2679 """
2719 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2680 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2720 repo.rollback()
2681 repo.rollback()
2721
2682
2722 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2683 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2723 branch=None):
2684 branch=None):
2724 """update or merge working directory
2685 """update or merge working directory
2725
2686
2726 Update the working directory to the specified revision.
2687 Update the working directory to the specified revision.
2727
2688
2728 If there are no outstanding changes in the working directory and
2689 If there are no outstanding changes in the working directory and
2729 there is a linear relationship between the current version and the
2690 there is a linear relationship between the current version and the
2730 requested version, the result is the requested version.
2691 requested version, the result is the requested version.
2731
2692
2732 To merge the working directory with another revision, use the
2693 To merge the working directory with another revision, use the
2733 merge command.
2694 merge command.
2734
2695
2735 By default, update will refuse to run if doing so would require
2696 By default, update will refuse to run if doing so would require
2736 merging or discarding local changes.
2697 merging or discarding local changes.
2737 """
2698 """
2738 node = _lookup(repo, node, branch)
2699 node = _lookup(repo, node, branch)
2739 if merge:
2700 if merge:
2740 ui.warn(_('(the -m/--merge option is deprecated; '
2701 ui.warn(_('(the -m/--merge option is deprecated; '
2741 'use the merge command instead)\n'))
2702 'use the merge command instead)\n'))
2742 return hg.merge(repo, node, force=force)
2703 return hg.merge(repo, node, force=force)
2743 elif clean:
2704 elif clean:
2744 return hg.clean(repo, node)
2705 return hg.clean(repo, node)
2745 else:
2706 else:
2746 return hg.update(repo, node)
2707 return hg.update(repo, node)
2747
2708
2748 def _lookup(repo, node, branch=None):
2709 def _lookup(repo, node, branch=None):
2749 if branch:
2710 if branch:
2750 br = repo.branchlookup(branch=branch)
2711 br = repo.branchlookup(branch=branch)
2751 found = []
2712 found = []
2752 for x in br:
2713 for x in br:
2753 if branch in br[x]:
2714 if branch in br[x]:
2754 found.append(x)
2715 found.append(x)
2755 if len(found) > 1:
2716 if len(found) > 1:
2756 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2717 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2757 for x in found:
2718 for x in found:
2758 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2719 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2759 raise util.Abort("")
2720 raise util.Abort("")
2760 if len(found) == 1:
2721 if len(found) == 1:
2761 node = found[0]
2722 node = found[0]
2762 repo.ui.warn(_("Using head %s for branch %s\n")
2723 repo.ui.warn(_("Using head %s for branch %s\n")
2763 % (short(node), branch))
2724 % (short(node), branch))
2764 else:
2725 else:
2765 raise util.Abort(_("branch %s not found\n") % (branch))
2726 raise util.Abort(_("branch %s not found\n") % (branch))
2766 else:
2727 else:
2767 node = node and repo.lookup(node) or repo.changelog.tip()
2728 node = node and repo.lookup(node) or repo.changelog.tip()
2768 return node
2729 return node
2769
2730
2770 def verify(ui, repo):
2731 def verify(ui, repo):
2771 """verify the integrity of the repository
2732 """verify the integrity of the repository
2772
2733
2773 Verify the integrity of the current repository.
2734 Verify the integrity of the current repository.
2774
2735
2775 This will perform an extensive check of the repository's
2736 This will perform an extensive check of the repository's
2776 integrity, validating the hashes and checksums of each entry in
2737 integrity, validating the hashes and checksums of each entry in
2777 the changelog, manifest, and tracked files, as well as the
2738 the changelog, manifest, and tracked files, as well as the
2778 integrity of their crosslinks and indices.
2739 integrity of their crosslinks and indices.
2779 """
2740 """
2780 return hg.verify(repo)
2741 return hg.verify(repo)
2781
2742
2782 # Command options and aliases are listed here, alphabetically
2743 # Command options and aliases are listed here, alphabetically
2783
2744
2784 table = {
2745 table = {
2785 "^add":
2746 "^add":
2786 (add,
2747 (add,
2787 [('I', 'include', [], _('include names matching the given patterns')),
2748 [('I', 'include', [], _('include names matching the given patterns')),
2788 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2749 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2789 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2750 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2790 _('hg add [OPTION]... [FILE]...')),
2751 _('hg add [OPTION]... [FILE]...')),
2791 "debugaddremove|addremove":
2752 "debugaddremove|addremove":
2792 (addremove,
2753 (addremove,
2793 [('I', 'include', [], _('include names matching the given patterns')),
2754 [('I', 'include', [], _('include names matching the given patterns')),
2794 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2755 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2795 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2756 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2796 _('hg addremove [OPTION]... [FILE]...')),
2757 _('hg addremove [OPTION]... [FILE]...')),
2797 "^annotate":
2758 "^annotate":
2798 (annotate,
2759 (annotate,
2799 [('r', 'rev', '', _('annotate the specified revision')),
2760 [('r', 'rev', '', _('annotate the specified revision')),
2800 ('a', 'text', None, _('treat all files as text')),
2761 ('a', 'text', None, _('treat all files as text')),
2801 ('u', 'user', None, _('list the author')),
2762 ('u', 'user', None, _('list the author')),
2802 ('d', 'date', None, _('list the date')),
2763 ('d', 'date', None, _('list the date')),
2803 ('n', 'number', None, _('list the revision number (default)')),
2764 ('n', 'number', None, _('list the revision number (default)')),
2804 ('c', 'changeset', None, _('list the changeset')),
2765 ('c', 'changeset', None, _('list the changeset')),
2805 ('I', 'include', [], _('include names matching the given patterns')),
2766 ('I', 'include', [], _('include names matching the given patterns')),
2806 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2767 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2807 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2768 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2808 "archive":
2769 "archive":
2809 (archive,
2770 (archive,
2810 [('', 'no-decode', None, _('do not pass files through decoders')),
2771 [('', 'no-decode', None, _('do not pass files through decoders')),
2811 ('p', 'prefix', '', _('directory prefix for files in archive')),
2772 ('p', 'prefix', '', _('directory prefix for files in archive')),
2812 ('r', 'rev', '', _('revision to distribute')),
2773 ('r', 'rev', '', _('revision to distribute')),
2813 ('t', 'type', '', _('type of distribution to create')),
2774 ('t', 'type', '', _('type of distribution to create')),
2814 ('I', 'include', [], _('include names matching the given patterns')),
2775 ('I', 'include', [], _('include names matching the given patterns')),
2815 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2776 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2816 _('hg archive [OPTION]... DEST')),
2777 _('hg archive [OPTION]... DEST')),
2817 "backout":
2778 "backout":
2818 (backout,
2779 (backout,
2819 [('', 'merge', None,
2780 [('', 'merge', None,
2820 _('merge with old dirstate parent after backout')),
2781 _('merge with old dirstate parent after backout')),
2821 ('m', 'message', '', _('use <text> as commit message')),
2782 ('m', 'message', '', _('use <text> as commit message')),
2822 ('l', 'logfile', '', _('read commit message from <file>')),
2783 ('l', 'logfile', '', _('read commit message from <file>')),
2823 ('d', 'date', '', _('record datecode as commit date')),
2784 ('d', 'date', '', _('record datecode as commit date')),
2824 ('', 'parent', '', _('parent to choose when backing out merge')),
2785 ('', 'parent', '', _('parent to choose when backing out merge')),
2825 ('u', 'user', '', _('record user as committer')),
2786 ('u', 'user', '', _('record user as committer')),
2826 ('I', 'include', [], _('include names matching the given patterns')),
2787 ('I', 'include', [], _('include names matching the given patterns')),
2827 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2788 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2828 _('hg backout [OPTION]... REV')),
2789 _('hg backout [OPTION]... REV')),
2829 "bundle":
2790 "bundle":
2830 (bundle,
2791 (bundle,
2831 [('f', 'force', None,
2792 [('f', 'force', None,
2832 _('run even when remote repository is unrelated'))],
2793 _('run even when remote repository is unrelated'))],
2833 _('hg bundle FILE DEST')),
2794 _('hg bundle FILE DEST')),
2834 "cat":
2795 "cat":
2835 (cat,
2796 (cat,
2836 [('o', 'output', '', _('print output to file with formatted name')),
2797 [('o', 'output', '', _('print output to file with formatted name')),
2837 ('r', 'rev', '', _('print the given revision')),
2798 ('r', 'rev', '', _('print the given revision')),
2838 ('I', 'include', [], _('include names matching the given patterns')),
2799 ('I', 'include', [], _('include names matching the given patterns')),
2839 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2800 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2840 _('hg cat [OPTION]... FILE...')),
2801 _('hg cat [OPTION]... FILE...')),
2841 "^clone":
2802 "^clone":
2842 (clone,
2803 (clone,
2843 [('U', 'noupdate', None, _('do not update the new working directory')),
2804 [('U', 'noupdate', None, _('do not update the new working directory')),
2844 ('r', 'rev', [],
2805 ('r', 'rev', [],
2845 _('a changeset you would like to have after cloning')),
2806 _('a changeset you would like to have after cloning')),
2846 ('', 'pull', None, _('use pull protocol to copy metadata')),
2807 ('', 'pull', None, _('use pull protocol to copy metadata')),
2847 ('', 'uncompressed', None,
2808 ('', 'uncompressed', None,
2848 _('use uncompressed transfer (fast over LAN)')),
2809 _('use uncompressed transfer (fast over LAN)')),
2849 ('e', 'ssh', '', _('specify ssh command to use')),
2810 ('e', 'ssh', '', _('specify ssh command to use')),
2850 ('', 'remotecmd', '',
2811 ('', 'remotecmd', '',
2851 _('specify hg command to run on the remote side'))],
2812 _('specify hg command to run on the remote side'))],
2852 _('hg clone [OPTION]... SOURCE [DEST]')),
2813 _('hg clone [OPTION]... SOURCE [DEST]')),
2853 "^commit|ci":
2814 "^commit|ci":
2854 (commit,
2815 (commit,
2855 [('A', 'addremove', None,
2816 [('A', 'addremove', None,
2856 _('mark new/missing files as added/removed before committing')),
2817 _('mark new/missing files as added/removed before committing')),
2857 ('m', 'message', '', _('use <text> as commit message')),
2818 ('m', 'message', '', _('use <text> as commit message')),
2858 ('l', 'logfile', '', _('read the commit message from <file>')),
2819 ('l', 'logfile', '', _('read the commit message from <file>')),
2859 ('d', 'date', '', _('record datecode as commit date')),
2820 ('d', 'date', '', _('record datecode as commit date')),
2860 ('u', 'user', '', _('record user as commiter')),
2821 ('u', 'user', '', _('record user as commiter')),
2861 ('I', 'include', [], _('include names matching the given patterns')),
2822 ('I', 'include', [], _('include names matching the given patterns')),
2862 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2823 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2863 _('hg commit [OPTION]... [FILE]...')),
2824 _('hg commit [OPTION]... [FILE]...')),
2864 "copy|cp":
2825 "copy|cp":
2865 (copy,
2826 (copy,
2866 [('A', 'after', None, _('record a copy that has already occurred')),
2827 [('A', 'after', None, _('record a copy that has already occurred')),
2867 ('f', 'force', None,
2828 ('f', 'force', None,
2868 _('forcibly copy over an existing managed file')),
2829 _('forcibly copy over an existing managed file')),
2869 ('I', 'include', [], _('include names matching the given patterns')),
2830 ('I', 'include', [], _('include names matching the given patterns')),
2870 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2831 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2871 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2832 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2872 _('hg copy [OPTION]... [SOURCE]... DEST')),
2833 _('hg copy [OPTION]... [SOURCE]... DEST')),
2873 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2834 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2874 "debugcomplete":
2835 "debugcomplete":
2875 (debugcomplete,
2836 (debugcomplete,
2876 [('o', 'options', None, _('show the command options'))],
2837 [('o', 'options', None, _('show the command options'))],
2877 _('debugcomplete [-o] CMD')),
2838 _('debugcomplete [-o] CMD')),
2878 "debugrebuildstate":
2839 "debugrebuildstate":
2879 (debugrebuildstate,
2840 (debugrebuildstate,
2880 [('r', 'rev', '', _('revision to rebuild to'))],
2841 [('r', 'rev', '', _('revision to rebuild to'))],
2881 _('debugrebuildstate [-r REV] [REV]')),
2842 _('debugrebuildstate [-r REV] [REV]')),
2882 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2843 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2883 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2844 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2884 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2845 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2885 "debugstate": (debugstate, [], _('debugstate')),
2846 "debugstate": (debugstate, [], _('debugstate')),
2886 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2847 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2887 "debugindex": (debugindex, [], _('debugindex FILE')),
2848 "debugindex": (debugindex, [], _('debugindex FILE')),
2888 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2849 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2889 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2850 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2890 "debugwalk":
2851 "debugwalk":
2891 (debugwalk,
2852 (debugwalk,
2892 [('I', 'include', [], _('include names matching the given patterns')),
2853 [('I', 'include', [], _('include names matching the given patterns')),
2893 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2854 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2894 _('debugwalk [OPTION]... [FILE]...')),
2855 _('debugwalk [OPTION]... [FILE]...')),
2895 "^diff":
2856 "^diff":
2896 (diff,
2857 (diff,
2897 [('r', 'rev', [], _('revision')),
2858 [('r', 'rev', [], _('revision')),
2898 ('a', 'text', None, _('treat all files as text')),
2859 ('a', 'text', None, _('treat all files as text')),
2899 ('p', 'show-function', None,
2860 ('p', 'show-function', None,
2900 _('show which function each change is in')),
2861 _('show which function each change is in')),
2901 ('g', 'git', None, _('use git extended diff format')),
2862 ('g', 'git', None, _('use git extended diff format')),
2902 ('w', 'ignore-all-space', None,
2863 ('w', 'ignore-all-space', None,
2903 _('ignore white space when comparing lines')),
2864 _('ignore white space when comparing lines')),
2904 ('b', 'ignore-space-change', None,
2865 ('b', 'ignore-space-change', None,
2905 _('ignore changes in the amount of white space')),
2866 _('ignore changes in the amount of white space')),
2906 ('B', 'ignore-blank-lines', None,
2867 ('B', 'ignore-blank-lines', None,
2907 _('ignore changes whose lines are all blank')),
2868 _('ignore changes whose lines are all blank')),
2908 ('I', 'include', [], _('include names matching the given patterns')),
2869 ('I', 'include', [], _('include names matching the given patterns')),
2909 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2870 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2910 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2871 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2911 "^export":
2872 "^export":
2912 (export,
2873 (export,
2913 [('o', 'output', '', _('print output to file with formatted name')),
2874 [('o', 'output', '', _('print output to file with formatted name')),
2914 ('a', 'text', None, _('treat all files as text')),
2875 ('a', 'text', None, _('treat all files as text')),
2915 ('', 'switch-parent', None, _('diff against the second parent'))],
2876 ('', 'switch-parent', None, _('diff against the second parent'))],
2916 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2877 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2917 "debugforget|forget":
2878 "debugforget|forget":
2918 (forget,
2879 (forget,
2919 [('I', 'include', [], _('include names matching the given patterns')),
2880 [('I', 'include', [], _('include names matching the given patterns')),
2920 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2881 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2921 _('hg forget [OPTION]... FILE...')),
2882 _('hg forget [OPTION]... FILE...')),
2922 "grep":
2883 "grep":
2923 (grep,
2884 (grep,
2924 [('0', 'print0', None, _('end fields with NUL')),
2885 [('0', 'print0', None, _('end fields with NUL')),
2925 ('', 'all', None, _('print all revisions that match')),
2886 ('', 'all', None, _('print all revisions that match')),
2926 ('f', 'follow', None,
2887 ('f', 'follow', None,
2927 _('follow changeset history, or file history across copies and renames')),
2888 _('follow changeset history, or file history across copies and renames')),
2928 ('i', 'ignore-case', None, _('ignore case when matching')),
2889 ('i', 'ignore-case', None, _('ignore case when matching')),
2929 ('l', 'files-with-matches', None,
2890 ('l', 'files-with-matches', None,
2930 _('print only filenames and revs that match')),
2891 _('print only filenames and revs that match')),
2931 ('n', 'line-number', None, _('print matching line numbers')),
2892 ('n', 'line-number', None, _('print matching line numbers')),
2932 ('r', 'rev', [], _('search in given revision range')),
2893 ('r', 'rev', [], _('search in given revision range')),
2933 ('u', 'user', None, _('print user who committed change')),
2894 ('u', 'user', None, _('print user who committed change')),
2934 ('I', 'include', [], _('include names matching the given patterns')),
2895 ('I', 'include', [], _('include names matching the given patterns')),
2935 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2896 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2936 _('hg grep [OPTION]... PATTERN [FILE]...')),
2897 _('hg grep [OPTION]... PATTERN [FILE]...')),
2937 "heads":
2898 "heads":
2938 (heads,
2899 (heads,
2939 [('b', 'branches', None, _('show branches')),
2900 [('b', 'branches', None, _('show branches')),
2940 ('', 'style', '', _('display using template map file')),
2901 ('', 'style', '', _('display using template map file')),
2941 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2902 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2942 ('', 'template', '', _('display with template'))],
2903 ('', 'template', '', _('display with template'))],
2943 _('hg heads [-b] [-r <rev>]')),
2904 _('hg heads [-b] [-r <rev>]')),
2944 "help": (help_, [], _('hg help [COMMAND]')),
2905 "help": (help_, [], _('hg help [COMMAND]')),
2945 "identify|id": (identify, [], _('hg identify')),
2906 "identify|id": (identify, [], _('hg identify')),
2946 "import|patch":
2907 "import|patch":
2947 (import_,
2908 (import_,
2948 [('p', 'strip', 1,
2909 [('p', 'strip', 1,
2949 _('directory strip option for patch. This has the same\n'
2910 _('directory strip option for patch. This has the same\n'
2950 'meaning as the corresponding patch option')),
2911 'meaning as the corresponding patch option')),
2951 ('m', 'message', '', _('use <text> as commit message')),
2912 ('m', 'message', '', _('use <text> as commit message')),
2952 ('b', 'base', '', _('base path')),
2913 ('b', 'base', '', _('base path')),
2953 ('f', 'force', None,
2914 ('f', 'force', None,
2954 _('skip check for outstanding uncommitted changes'))],
2915 _('skip check for outstanding uncommitted changes'))],
2955 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2916 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2956 "incoming|in": (incoming,
2917 "incoming|in": (incoming,
2957 [('M', 'no-merges', None, _('do not show merges')),
2918 [('M', 'no-merges', None, _('do not show merges')),
2958 ('f', 'force', None,
2919 ('f', 'force', None,
2959 _('run even when remote repository is unrelated')),
2920 _('run even when remote repository is unrelated')),
2960 ('', 'style', '', _('display using template map file')),
2921 ('', 'style', '', _('display using template map file')),
2961 ('n', 'newest-first', None, _('show newest record first')),
2922 ('n', 'newest-first', None, _('show newest record first')),
2962 ('', 'bundle', '', _('file to store the bundles into')),
2923 ('', 'bundle', '', _('file to store the bundles into')),
2963 ('p', 'patch', None, _('show patch')),
2924 ('p', 'patch', None, _('show patch')),
2964 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2925 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2965 ('', 'template', '', _('display with template')),
2926 ('', 'template', '', _('display with template')),
2966 ('e', 'ssh', '', _('specify ssh command to use')),
2927 ('e', 'ssh', '', _('specify ssh command to use')),
2967 ('', 'remotecmd', '',
2928 ('', 'remotecmd', '',
2968 _('specify hg command to run on the remote side'))],
2929 _('specify hg command to run on the remote side'))],
2969 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2930 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2970 ' [--bundle FILENAME] [SOURCE]')),
2931 ' [--bundle FILENAME] [SOURCE]')),
2971 "^init":
2932 "^init":
2972 (init,
2933 (init,
2973 [('e', 'ssh', '', _('specify ssh command to use')),
2934 [('e', 'ssh', '', _('specify ssh command to use')),
2974 ('', 'remotecmd', '',
2935 ('', 'remotecmd', '',
2975 _('specify hg command to run on the remote side'))],
2936 _('specify hg command to run on the remote side'))],
2976 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2937 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2977 "locate":
2938 "locate":
2978 (locate,
2939 (locate,
2979 [('r', 'rev', '', _('search the repository as it stood at rev')),
2940 [('r', 'rev', '', _('search the repository as it stood at rev')),
2980 ('0', 'print0', None,
2941 ('0', 'print0', None,
2981 _('end filenames with NUL, for use with xargs')),
2942 _('end filenames with NUL, for use with xargs')),
2982 ('f', 'fullpath', None,
2943 ('f', 'fullpath', None,
2983 _('print complete paths from the filesystem root')),
2944 _('print complete paths from the filesystem root')),
2984 ('I', 'include', [], _('include names matching the given patterns')),
2945 ('I', 'include', [], _('include names matching the given patterns')),
2985 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2946 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2986 _('hg locate [OPTION]... [PATTERN]...')),
2947 _('hg locate [OPTION]... [PATTERN]...')),
2987 "^log|history":
2948 "^log|history":
2988 (log,
2949 (log,
2989 [('b', 'branches', None, _('show branches')),
2950 [('b', 'branches', None, _('show branches')),
2990 ('f', 'follow', None,
2951 ('f', 'follow', None,
2991 _('follow changeset history, or file history across copies and renames')),
2952 _('follow changeset history, or file history across copies and renames')),
2992 ('', 'follow-first', None,
2953 ('', 'follow-first', None,
2993 _('only follow the first parent of merge changesets')),
2954 _('only follow the first parent of merge changesets')),
2994 ('k', 'keyword', [], _('search for a keyword')),
2955 ('k', 'keyword', [], _('search for a keyword')),
2995 ('l', 'limit', '', _('limit number of changes displayed')),
2956 ('l', 'limit', '', _('limit number of changes displayed')),
2996 ('r', 'rev', [], _('show the specified revision or range')),
2957 ('r', 'rev', [], _('show the specified revision or range')),
2997 ('M', 'no-merges', None, _('do not show merges')),
2958 ('M', 'no-merges', None, _('do not show merges')),
2998 ('', 'style', '', _('display using template map file')),
2959 ('', 'style', '', _('display using template map file')),
2999 ('m', 'only-merges', None, _('show only merges')),
2960 ('m', 'only-merges', None, _('show only merges')),
3000 ('p', 'patch', None, _('show patch')),
2961 ('p', 'patch', None, _('show patch')),
3001 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2962 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3002 ('', 'template', '', _('display with template')),
2963 ('', 'template', '', _('display with template')),
3003 ('I', 'include', [], _('include names matching the given patterns')),
2964 ('I', 'include', [], _('include names matching the given patterns')),
3004 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2965 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3005 _('hg log [OPTION]... [FILE]')),
2966 _('hg log [OPTION]... [FILE]')),
3006 "manifest": (manifest, [], _('hg manifest [REV]')),
2967 "manifest": (manifest, [], _('hg manifest [REV]')),
3007 "merge":
2968 "merge":
3008 (merge,
2969 (merge,
3009 [('b', 'branch', '', _('merge with head of a specific branch')),
2970 [('b', 'branch', '', _('merge with head of a specific branch')),
3010 ('f', 'force', None, _('force a merge with outstanding changes'))],
2971 ('f', 'force', None, _('force a merge with outstanding changes'))],
3011 _('hg merge [-b TAG] [-f] [REV]')),
2972 _('hg merge [-b TAG] [-f] [REV]')),
3012 "outgoing|out": (outgoing,
2973 "outgoing|out": (outgoing,
3013 [('M', 'no-merges', None, _('do not show merges')),
2974 [('M', 'no-merges', None, _('do not show merges')),
3014 ('f', 'force', None,
2975 ('f', 'force', None,
3015 _('run even when remote repository is unrelated')),
2976 _('run even when remote repository is unrelated')),
3016 ('p', 'patch', None, _('show patch')),
2977 ('p', 'patch', None, _('show patch')),
3017 ('', 'style', '', _('display using template map file')),
2978 ('', 'style', '', _('display using template map file')),
3018 ('r', 'rev', [], _('a specific revision you would like to push')),
2979 ('r', 'rev', [], _('a specific revision you would like to push')),
3019 ('n', 'newest-first', None, _('show newest record first')),
2980 ('n', 'newest-first', None, _('show newest record first')),
3020 ('', 'template', '', _('display with template')),
2981 ('', 'template', '', _('display with template')),
3021 ('e', 'ssh', '', _('specify ssh command to use')),
2982 ('e', 'ssh', '', _('specify ssh command to use')),
3022 ('', 'remotecmd', '',
2983 ('', 'remotecmd', '',
3023 _('specify hg command to run on the remote side'))],
2984 _('specify hg command to run on the remote side'))],
3024 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2985 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3025 "^parents":
2986 "^parents":
3026 (parents,
2987 (parents,
3027 [('b', 'branches', None, _('show branches')),
2988 [('b', 'branches', None, _('show branches')),
3028 ('r', 'rev', '', _('show parents from the specified rev')),
2989 ('r', 'rev', '', _('show parents from the specified rev')),
3029 ('', 'style', '', _('display using template map file')),
2990 ('', 'style', '', _('display using template map file')),
3030 ('', 'template', '', _('display with template'))],
2991 ('', 'template', '', _('display with template'))],
3031 _('hg parents [-b] [-r REV] [FILE]')),
2992 _('hg parents [-b] [-r REV] [FILE]')),
3032 "paths": (paths, [], _('hg paths [NAME]')),
2993 "paths": (paths, [], _('hg paths [NAME]')),
3033 "^pull":
2994 "^pull":
3034 (pull,
2995 (pull,
3035 [('u', 'update', None,
2996 [('u', 'update', None,
3036 _('update the working directory to tip after pull')),
2997 _('update the working directory to tip after pull')),
3037 ('e', 'ssh', '', _('specify ssh command to use')),
2998 ('e', 'ssh', '', _('specify ssh command to use')),
3038 ('f', 'force', None,
2999 ('f', 'force', None,
3039 _('run even when remote repository is unrelated')),
3000 _('run even when remote repository is unrelated')),
3040 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3001 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3041 ('', 'remotecmd', '',
3002 ('', 'remotecmd', '',
3042 _('specify hg command to run on the remote side'))],
3003 _('specify hg command to run on the remote side'))],
3043 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3004 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3044 "^push":
3005 "^push":
3045 (push,
3006 (push,
3046 [('f', 'force', None, _('force push')),
3007 [('f', 'force', None, _('force push')),
3047 ('e', 'ssh', '', _('specify ssh command to use')),
3008 ('e', 'ssh', '', _('specify ssh command to use')),
3048 ('r', 'rev', [], _('a specific revision you would like to push')),
3009 ('r', 'rev', [], _('a specific revision you would like to push')),
3049 ('', 'remotecmd', '',
3010 ('', 'remotecmd', '',
3050 _('specify hg command to run on the remote side'))],
3011 _('specify hg command to run on the remote side'))],
3051 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3012 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3052 "debugrawcommit|rawcommit":
3013 "debugrawcommit|rawcommit":
3053 (rawcommit,
3014 (rawcommit,
3054 [('p', 'parent', [], _('parent')),
3015 [('p', 'parent', [], _('parent')),
3055 ('d', 'date', '', _('date code')),
3016 ('d', 'date', '', _('date code')),
3056 ('u', 'user', '', _('user')),
3017 ('u', 'user', '', _('user')),
3057 ('F', 'files', '', _('file list')),
3018 ('F', 'files', '', _('file list')),
3058 ('m', 'message', '', _('commit message')),
3019 ('m', 'message', '', _('commit message')),
3059 ('l', 'logfile', '', _('commit message file'))],
3020 ('l', 'logfile', '', _('commit message file'))],
3060 _('hg debugrawcommit [OPTION]... [FILE]...')),
3021 _('hg debugrawcommit [OPTION]... [FILE]...')),
3061 "recover": (recover, [], _('hg recover')),
3022 "recover": (recover, [], _('hg recover')),
3062 "^remove|rm":
3023 "^remove|rm":
3063 (remove,
3024 (remove,
3064 [('A', 'after', None, _('record remove that has already occurred')),
3025 [('A', 'after', None, _('record remove that has already occurred')),
3065 ('f', 'force', None, _('remove file even if modified')),
3026 ('f', 'force', None, _('remove file even if modified')),
3066 ('I', 'include', [], _('include names matching the given patterns')),
3027 ('I', 'include', [], _('include names matching the given patterns')),
3067 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3028 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3068 _('hg remove [OPTION]... FILE...')),
3029 _('hg remove [OPTION]... FILE...')),
3069 "rename|mv":
3030 "rename|mv":
3070 (rename,
3031 (rename,
3071 [('A', 'after', None, _('record a rename that has already occurred')),
3032 [('A', 'after', None, _('record a rename that has already occurred')),
3072 ('f', 'force', None,
3033 ('f', 'force', None,
3073 _('forcibly copy over an existing managed file')),
3034 _('forcibly copy over an existing managed file')),
3074 ('I', 'include', [], _('include names matching the given patterns')),
3035 ('I', 'include', [], _('include names matching the given patterns')),
3075 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3036 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3076 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3037 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3077 _('hg rename [OPTION]... SOURCE... DEST')),
3038 _('hg rename [OPTION]... SOURCE... DEST')),
3078 "^revert":
3039 "^revert":
3079 (revert,
3040 (revert,
3080 [('r', 'rev', '', _('revision to revert to')),
3041 [('r', 'rev', '', _('revision to revert to')),
3081 ('', 'no-backup', None, _('do not save backup copies of files')),
3042 ('', 'no-backup', None, _('do not save backup copies of files')),
3082 ('I', 'include', [], _('include names matching given patterns')),
3043 ('I', 'include', [], _('include names matching given patterns')),
3083 ('X', 'exclude', [], _('exclude names matching given patterns')),
3044 ('X', 'exclude', [], _('exclude names matching given patterns')),
3084 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3045 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3085 _('hg revert [-r REV] [NAME]...')),
3046 _('hg revert [-r REV] [NAME]...')),
3086 "rollback": (rollback, [], _('hg rollback')),
3047 "rollback": (rollback, [], _('hg rollback')),
3087 "root": (root, [], _('hg root')),
3048 "root": (root, [], _('hg root')),
3088 "^serve":
3049 "^serve":
3089 (serve,
3050 (serve,
3090 [('A', 'accesslog', '', _('name of access log file to write to')),
3051 [('A', 'accesslog', '', _('name of access log file to write to')),
3091 ('d', 'daemon', None, _('run server in background')),
3052 ('d', 'daemon', None, _('run server in background')),
3092 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3053 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3093 ('E', 'errorlog', '', _('name of error log file to write to')),
3054 ('E', 'errorlog', '', _('name of error log file to write to')),
3094 ('p', 'port', 0, _('port to use (default: 8000)')),
3055 ('p', 'port', 0, _('port to use (default: 8000)')),
3095 ('a', 'address', '', _('address to use')),
3056 ('a', 'address', '', _('address to use')),
3096 ('n', 'name', '',
3057 ('n', 'name', '',
3097 _('name to show in web pages (default: working dir)')),
3058 _('name to show in web pages (default: working dir)')),
3098 ('', 'webdir-conf', '', _('name of the webdir config file'
3059 ('', 'webdir-conf', '', _('name of the webdir config file'
3099 ' (serve more than one repo)')),
3060 ' (serve more than one repo)')),
3100 ('', 'pid-file', '', _('name of file to write process ID to')),
3061 ('', 'pid-file', '', _('name of file to write process ID to')),
3101 ('', 'stdio', None, _('for remote clients')),
3062 ('', 'stdio', None, _('for remote clients')),
3102 ('t', 'templates', '', _('web templates to use')),
3063 ('t', 'templates', '', _('web templates to use')),
3103 ('', 'style', '', _('template style to use')),
3064 ('', 'style', '', _('template style to use')),
3104 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3065 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3105 _('hg serve [OPTION]...')),
3066 _('hg serve [OPTION]...')),
3106 "^status|st":
3067 "^status|st":
3107 (status,
3068 (status,
3108 [('A', 'all', None, _('show status of all files')),
3069 [('A', 'all', None, _('show status of all files')),
3109 ('m', 'modified', None, _('show only modified files')),
3070 ('m', 'modified', None, _('show only modified files')),
3110 ('a', 'added', None, _('show only added files')),
3071 ('a', 'added', None, _('show only added files')),
3111 ('r', 'removed', None, _('show only removed files')),
3072 ('r', 'removed', None, _('show only removed files')),
3112 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3073 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3113 ('c', 'clean', None, _('show only files without changes')),
3074 ('c', 'clean', None, _('show only files without changes')),
3114 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3075 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3115 ('i', 'ignored', None, _('show ignored files')),
3076 ('i', 'ignored', None, _('show ignored files')),
3116 ('n', 'no-status', None, _('hide status prefix')),
3077 ('n', 'no-status', None, _('hide status prefix')),
3117 ('C', 'copies', None, _('show source of copied files')),
3078 ('C', 'copies', None, _('show source of copied files')),
3118 ('0', 'print0', None,
3079 ('0', 'print0', None,
3119 _('end filenames with NUL, for use with xargs')),
3080 _('end filenames with NUL, for use with xargs')),
3120 ('I', 'include', [], _('include names matching the given patterns')),
3081 ('I', 'include', [], _('include names matching the given patterns')),
3121 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3082 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3122 _('hg status [OPTION]... [FILE]...')),
3083 _('hg status [OPTION]... [FILE]...')),
3123 "tag":
3084 "tag":
3124 (tag,
3085 (tag,
3125 [('l', 'local', None, _('make the tag local')),
3086 [('l', 'local', None, _('make the tag local')),
3126 ('m', 'message', '', _('message for tag commit log entry')),
3087 ('m', 'message', '', _('message for tag commit log entry')),
3127 ('d', 'date', '', _('record datecode as commit date')),
3088 ('d', 'date', '', _('record datecode as commit date')),
3128 ('u', 'user', '', _('record user as commiter')),
3089 ('u', 'user', '', _('record user as commiter')),
3129 ('r', 'rev', '', _('revision to tag'))],
3090 ('r', 'rev', '', _('revision to tag'))],
3130 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3091 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3131 "tags": (tags, [], _('hg tags')),
3092 "tags": (tags, [], _('hg tags')),
3132 "tip":
3093 "tip":
3133 (tip,
3094 (tip,
3134 [('b', 'branches', None, _('show branches')),
3095 [('b', 'branches', None, _('show branches')),
3135 ('', 'style', '', _('display using template map file')),
3096 ('', 'style', '', _('display using template map file')),
3136 ('p', 'patch', None, _('show patch')),
3097 ('p', 'patch', None, _('show patch')),
3137 ('', 'template', '', _('display with template'))],
3098 ('', 'template', '', _('display with template'))],
3138 _('hg tip [-b] [-p]')),
3099 _('hg tip [-b] [-p]')),
3139 "unbundle":
3100 "unbundle":
3140 (unbundle,
3101 (unbundle,
3141 [('u', 'update', None,
3102 [('u', 'update', None,
3142 _('update the working directory to tip after unbundle'))],
3103 _('update the working directory to tip after unbundle'))],
3143 _('hg unbundle [-u] FILE')),
3104 _('hg unbundle [-u] FILE')),
3144 "debugundo|undo": (undo, [], _('hg undo')),
3105 "debugundo|undo": (undo, [], _('hg undo')),
3145 "^update|up|checkout|co":
3106 "^update|up|checkout|co":
3146 (update,
3107 (update,
3147 [('b', 'branch', '', _('checkout the head of a specific branch')),
3108 [('b', 'branch', '', _('checkout the head of a specific branch')),
3148 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3109 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3149 ('C', 'clean', None, _('overwrite locally modified files')),
3110 ('C', 'clean', None, _('overwrite locally modified files')),
3150 ('f', 'force', None, _('force a merge with outstanding changes'))],
3111 ('f', 'force', None, _('force a merge with outstanding changes'))],
3151 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3112 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3152 "verify": (verify, [], _('hg verify')),
3113 "verify": (verify, [], _('hg verify')),
3153 "version": (show_version, [], _('hg version')),
3114 "version": (show_version, [], _('hg version')),
3154 }
3115 }
3155
3116
3156 globalopts = [
3117 globalopts = [
3157 ('R', 'repository', '',
3118 ('R', 'repository', '',
3158 _('repository root directory or symbolic path name')),
3119 _('repository root directory or symbolic path name')),
3159 ('', 'cwd', '', _('change working directory')),
3120 ('', 'cwd', '', _('change working directory')),
3160 ('y', 'noninteractive', None,
3121 ('y', 'noninteractive', None,
3161 _('do not prompt, assume \'yes\' for any required answers')),
3122 _('do not prompt, assume \'yes\' for any required answers')),
3162 ('q', 'quiet', None, _('suppress output')),
3123 ('q', 'quiet', None, _('suppress output')),
3163 ('v', 'verbose', None, _('enable additional output')),
3124 ('v', 'verbose', None, _('enable additional output')),
3164 ('', 'config', [], _('set/override config option')),
3125 ('', 'config', [], _('set/override config option')),
3165 ('', 'debug', None, _('enable debugging output')),
3126 ('', 'debug', None, _('enable debugging output')),
3166 ('', 'debugger', None, _('start debugger')),
3127 ('', 'debugger', None, _('start debugger')),
3167 ('', 'lsprof', None, _('print improved command execution profile')),
3128 ('', 'lsprof', None, _('print improved command execution profile')),
3168 ('', 'traceback', None, _('print traceback on exception')),
3129 ('', 'traceback', None, _('print traceback on exception')),
3169 ('', 'time', None, _('time how long the command takes')),
3130 ('', 'time', None, _('time how long the command takes')),
3170 ('', 'profile', None, _('print command execution profile')),
3131 ('', 'profile', None, _('print command execution profile')),
3171 ('', 'version', None, _('output version information and exit')),
3132 ('', 'version', None, _('output version information and exit')),
3172 ('h', 'help', None, _('display help and exit')),
3133 ('h', 'help', None, _('display help and exit')),
3173 ]
3134 ]
3174
3135
3175 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3136 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3176 " debugindex debugindexdot")
3137 " debugindex debugindexdot")
3177 optionalrepo = ("paths serve debugconfig")
3138 optionalrepo = ("paths serve debugconfig")
3178
3139
3179 def findpossible(cmd):
3140 def findpossible(cmd):
3180 """
3141 """
3181 Return cmd -> (aliases, command table entry)
3142 Return cmd -> (aliases, command table entry)
3182 for each matching command.
3143 for each matching command.
3183 Return debug commands (or their aliases) only if no normal command matches.
3144 Return debug commands (or their aliases) only if no normal command matches.
3184 """
3145 """
3185 choice = {}
3146 choice = {}
3186 debugchoice = {}
3147 debugchoice = {}
3187 for e in table.keys():
3148 for e in table.keys():
3188 aliases = e.lstrip("^").split("|")
3149 aliases = e.lstrip("^").split("|")
3189 found = None
3150 found = None
3190 if cmd in aliases:
3151 if cmd in aliases:
3191 found = cmd
3152 found = cmd
3192 else:
3153 else:
3193 for a in aliases:
3154 for a in aliases:
3194 if a.startswith(cmd):
3155 if a.startswith(cmd):
3195 found = a
3156 found = a
3196 break
3157 break
3197 if found is not None:
3158 if found is not None:
3198 if aliases[0].startswith("debug"):
3159 if aliases[0].startswith("debug"):
3199 debugchoice[found] = (aliases, table[e])
3160 debugchoice[found] = (aliases, table[e])
3200 else:
3161 else:
3201 choice[found] = (aliases, table[e])
3162 choice[found] = (aliases, table[e])
3202
3163
3203 if not choice and debugchoice:
3164 if not choice and debugchoice:
3204 choice = debugchoice
3165 choice = debugchoice
3205
3166
3206 return choice
3167 return choice
3207
3168
3208 def findcmd(cmd):
3169 def findcmd(cmd):
3209 """Return (aliases, command table entry) for command string."""
3170 """Return (aliases, command table entry) for command string."""
3210 choice = findpossible(cmd)
3171 choice = findpossible(cmd)
3211
3172
3212 if choice.has_key(cmd):
3173 if choice.has_key(cmd):
3213 return choice[cmd]
3174 return choice[cmd]
3214
3175
3215 if len(choice) > 1:
3176 if len(choice) > 1:
3216 clist = choice.keys()
3177 clist = choice.keys()
3217 clist.sort()
3178 clist.sort()
3218 raise AmbiguousCommand(cmd, clist)
3179 raise AmbiguousCommand(cmd, clist)
3219
3180
3220 if choice:
3181 if choice:
3221 return choice.values()[0]
3182 return choice.values()[0]
3222
3183
3223 raise UnknownCommand(cmd)
3184 raise UnknownCommand(cmd)
3224
3185
3225 def catchterm(*args):
3186 def catchterm(*args):
3226 raise util.SignalInterrupt
3187 raise util.SignalInterrupt
3227
3188
3228 def run():
3189 def run():
3229 sys.exit(dispatch(sys.argv[1:]))
3190 sys.exit(dispatch(sys.argv[1:]))
3230
3191
3231 class ParseError(Exception):
3192 class ParseError(Exception):
3232 """Exception raised on errors in parsing the command line."""
3193 """Exception raised on errors in parsing the command line."""
3233
3194
3234 def parse(ui, args):
3195 def parse(ui, args):
3235 options = {}
3196 options = {}
3236 cmdoptions = {}
3197 cmdoptions = {}
3237
3198
3238 try:
3199 try:
3239 args = fancyopts.fancyopts(args, globalopts, options)
3200 args = fancyopts.fancyopts(args, globalopts, options)
3240 except fancyopts.getopt.GetoptError, inst:
3201 except fancyopts.getopt.GetoptError, inst:
3241 raise ParseError(None, inst)
3202 raise ParseError(None, inst)
3242
3203
3243 if args:
3204 if args:
3244 cmd, args = args[0], args[1:]
3205 cmd, args = args[0], args[1:]
3245 aliases, i = findcmd(cmd)
3206 aliases, i = findcmd(cmd)
3246 cmd = aliases[0]
3207 cmd = aliases[0]
3247 defaults = ui.config("defaults", cmd)
3208 defaults = ui.config("defaults", cmd)
3248 if defaults:
3209 if defaults:
3249 args = defaults.split() + args
3210 args = defaults.split() + args
3250 c = list(i[1])
3211 c = list(i[1])
3251 else:
3212 else:
3252 cmd = None
3213 cmd = None
3253 c = []
3214 c = []
3254
3215
3255 # combine global options into local
3216 # combine global options into local
3256 for o in globalopts:
3217 for o in globalopts:
3257 c.append((o[0], o[1], options[o[1]], o[3]))
3218 c.append((o[0], o[1], options[o[1]], o[3]))
3258
3219
3259 try:
3220 try:
3260 args = fancyopts.fancyopts(args, c, cmdoptions)
3221 args = fancyopts.fancyopts(args, c, cmdoptions)
3261 except fancyopts.getopt.GetoptError, inst:
3222 except fancyopts.getopt.GetoptError, inst:
3262 raise ParseError(cmd, inst)
3223 raise ParseError(cmd, inst)
3263
3224
3264 # separate global options back out
3225 # separate global options back out
3265 for o in globalopts:
3226 for o in globalopts:
3266 n = o[1]
3227 n = o[1]
3267 options[n] = cmdoptions[n]
3228 options[n] = cmdoptions[n]
3268 del cmdoptions[n]
3229 del cmdoptions[n]
3269
3230
3270 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3231 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3271
3232
3272 external = {}
3233 external = {}
3273
3234
3274 def findext(name):
3235 def findext(name):
3275 '''return module with given extension name'''
3236 '''return module with given extension name'''
3276 try:
3237 try:
3277 return sys.modules[external[name]]
3238 return sys.modules[external[name]]
3278 except KeyError:
3239 except KeyError:
3279 for k, v in external.iteritems():
3240 for k, v in external.iteritems():
3280 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3241 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3281 return sys.modules[v]
3242 return sys.modules[v]
3282 raise KeyError(name)
3243 raise KeyError(name)
3283
3244
3284 def dispatch(args):
3245 def load_extensions(ui):
3285 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3246 added = []
3286 num = getattr(signal, name, None)
3247 for ext_name, load_from_name in ui.extensions():
3287 if num: signal.signal(num, catchterm)
3248 if ext_name in external:
3288
3249 continue
3289 try:
3290 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3291 except util.Abort, inst:
3292 sys.stderr.write(_("abort: %s\n") % inst)
3293 return -1
3294
3295 for ext_name, load_from_name in u.extensions():
3296 try:
3250 try:
3297 if load_from_name:
3251 if load_from_name:
3298 # the module will be loaded in sys.modules
3252 # the module will be loaded in sys.modules
3299 # choose an unique name so that it doesn't
3253 # choose an unique name so that it doesn't
3300 # conflicts with other modules
3254 # conflicts with other modules
3301 module_name = "hgext_%s" % ext_name.replace('.', '_')
3255 module_name = "hgext_%s" % ext_name.replace('.', '_')
3302 mod = imp.load_source(module_name, load_from_name)
3256 mod = imp.load_source(module_name, load_from_name)
3303 else:
3257 else:
3304 def importh(name):
3258 def importh(name):
3305 mod = __import__(name)
3259 mod = __import__(name)
3306 components = name.split('.')
3260 components = name.split('.')
3307 for comp in components[1:]:
3261 for comp in components[1:]:
3308 mod = getattr(mod, comp)
3262 mod = getattr(mod, comp)
3309 return mod
3263 return mod
3310 try:
3264 try:
3311 mod = importh("hgext.%s" % ext_name)
3265 mod = importh("hgext.%s" % ext_name)
3312 except ImportError:
3266 except ImportError:
3313 mod = importh(ext_name)
3267 mod = importh(ext_name)
3314 external[ext_name] = mod.__name__
3268 external[ext_name] = mod.__name__
3269 added.append((mod, ext_name))
3315 except (util.SignalInterrupt, KeyboardInterrupt):
3270 except (util.SignalInterrupt, KeyboardInterrupt):
3316 raise
3271 raise
3317 except Exception, inst:
3272 except Exception, inst:
3318 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3273 ui.warn(_("*** failed to import extension %s: %s\n") %
3319 if u.print_exc():
3274 (ext_name, inst))
3275 if ui.print_exc():
3320 return 1
3276 return 1
3321
3277
3322 for name in external.itervalues():
3278 for mod, name in added:
3323 mod = sys.modules[name]
3324 uisetup = getattr(mod, 'uisetup', None)
3279 uisetup = getattr(mod, 'uisetup', None)
3325 if uisetup:
3280 if uisetup:
3326 uisetup(u)
3281 uisetup(ui)
3327 cmdtable = getattr(mod, 'cmdtable', {})
3282 cmdtable = getattr(mod, 'cmdtable', {})
3328 for t in cmdtable:
3283 for t in cmdtable:
3329 if t in table:
3284 if t in table:
3330 u.warn(_("module %s overrides %s\n") % (name, t))
3285 ui.warn(_("module %s overrides %s\n") % (name, t))
3331 table.update(cmdtable)
3286 table.update(cmdtable)
3287
3288 def dispatch(args):
3289 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3290 num = getattr(signal, name, None)
3291 if num: signal.signal(num, catchterm)
3292
3293 try:
3294 u = ui.ui(traceback='--traceback' in sys.argv[1:],
3295 readhooks=[load_extensions])
3296 except util.Abort, inst:
3297 sys.stderr.write(_("abort: %s\n") % inst)
3298 return -1
3332
3299
3333 try:
3300 try:
3334 cmd, func, args, options, cmdoptions = parse(u, args)
3301 cmd, func, args, options, cmdoptions = parse(u, args)
3335 if options["time"]:
3302 if options["time"]:
3336 def get_times():
3303 def get_times():
3337 t = os.times()
3304 t = os.times()
3338 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3305 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3339 t = (t[0], t[1], t[2], t[3], time.clock())
3306 t = (t[0], t[1], t[2], t[3], time.clock())
3340 return t
3307 return t
3341 s = get_times()
3308 s = get_times()
3342 def print_time():
3309 def print_time():
3343 t = get_times()
3310 t = get_times()
3344 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3311 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3345 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3312 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3346 atexit.register(print_time)
3313 atexit.register(print_time)
3347
3314
3348 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3315 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3349 not options["noninteractive"], options["traceback"],
3316 not options["noninteractive"], options["traceback"],
3350 options["config"])
3317 options["config"])
3351
3318
3352 # enter the debugger before command execution
3319 # enter the debugger before command execution
3353 if options['debugger']:
3320 if options['debugger']:
3354 pdb.set_trace()
3321 pdb.set_trace()
3355
3322
3356 try:
3323 try:
3357 if options['cwd']:
3324 if options['cwd']:
3358 try:
3325 try:
3359 os.chdir(options['cwd'])
3326 os.chdir(options['cwd'])
3360 except OSError, inst:
3327 except OSError, inst:
3361 raise util.Abort('%s: %s' %
3328 raise util.Abort('%s: %s' %
3362 (options['cwd'], inst.strerror))
3329 (options['cwd'], inst.strerror))
3363
3330
3364 path = u.expandpath(options["repository"]) or ""
3331 path = u.expandpath(options["repository"]) or ""
3365 repo = path and hg.repository(u, path=path) or None
3332 repo = path and hg.repository(u, path=path) or None
3366
3333
3367 if options['help']:
3334 if options['help']:
3368 return help_(u, cmd, options['version'])
3335 return help_(u, cmd, options['version'])
3369 elif options['version']:
3336 elif options['version']:
3370 return show_version(u)
3337 return show_version(u)
3371 elif not cmd:
3338 elif not cmd:
3372 return help_(u, 'shortlist')
3339 return help_(u, 'shortlist')
3373
3340
3374 if cmd not in norepo.split():
3341 if cmd not in norepo.split():
3375 try:
3342 try:
3376 if not repo:
3343 if not repo:
3377 repo = hg.repository(u, path=path)
3344 repo = hg.repository(u, path=path)
3378 u = repo.ui
3345 u = repo.ui
3379 for name in external.itervalues():
3346 for name in external.itervalues():
3380 mod = sys.modules[name]
3347 mod = sys.modules[name]
3381 if hasattr(mod, 'reposetup'):
3348 if hasattr(mod, 'reposetup'):
3382 mod.reposetup(u, repo)
3349 mod.reposetup(u, repo)
3383 hg.repo_setup_hooks.append(mod.reposetup)
3350 hg.repo_setup_hooks.append(mod.reposetup)
3384 except hg.RepoError:
3351 except hg.RepoError:
3385 if cmd not in optionalrepo.split():
3352 if cmd not in optionalrepo.split():
3386 raise
3353 raise
3387 d = lambda: func(u, repo, *args, **cmdoptions)
3354 d = lambda: func(u, repo, *args, **cmdoptions)
3388 else:
3355 else:
3389 d = lambda: func(u, *args, **cmdoptions)
3356 d = lambda: func(u, *args, **cmdoptions)
3390
3357
3391 # reupdate the options, repo/.hg/hgrc may have changed them
3358 # reupdate the options, repo/.hg/hgrc may have changed them
3392 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3359 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3393 not options["noninteractive"], options["traceback"],
3360 not options["noninteractive"], options["traceback"],
3394 options["config"])
3361 options["config"])
3395
3362
3396 try:
3363 try:
3397 if options['profile']:
3364 if options['profile']:
3398 import hotshot, hotshot.stats
3365 import hotshot, hotshot.stats
3399 prof = hotshot.Profile("hg.prof")
3366 prof = hotshot.Profile("hg.prof")
3400 try:
3367 try:
3401 try:
3368 try:
3402 return prof.runcall(d)
3369 return prof.runcall(d)
3403 except:
3370 except:
3404 try:
3371 try:
3405 u.warn(_('exception raised - generating '
3372 u.warn(_('exception raised - generating '
3406 'profile anyway\n'))
3373 'profile anyway\n'))
3407 except:
3374 except:
3408 pass
3375 pass
3409 raise
3376 raise
3410 finally:
3377 finally:
3411 prof.close()
3378 prof.close()
3412 stats = hotshot.stats.load("hg.prof")
3379 stats = hotshot.stats.load("hg.prof")
3413 stats.strip_dirs()
3380 stats.strip_dirs()
3414 stats.sort_stats('time', 'calls')
3381 stats.sort_stats('time', 'calls')
3415 stats.print_stats(40)
3382 stats.print_stats(40)
3416 elif options['lsprof']:
3383 elif options['lsprof']:
3417 try:
3384 try:
3418 from mercurial import lsprof
3385 from mercurial import lsprof
3419 except ImportError:
3386 except ImportError:
3420 raise util.Abort(_(
3387 raise util.Abort(_(
3421 'lsprof not available - install from '
3388 'lsprof not available - install from '
3422 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3389 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3423 p = lsprof.Profiler()
3390 p = lsprof.Profiler()
3424 p.enable(subcalls=True)
3391 p.enable(subcalls=True)
3425 try:
3392 try:
3426 return d()
3393 return d()
3427 finally:
3394 finally:
3428 p.disable()
3395 p.disable()
3429 stats = lsprof.Stats(p.getstats())
3396 stats = lsprof.Stats(p.getstats())
3430 stats.sort()
3397 stats.sort()
3431 stats.pprint(top=10, file=sys.stderr, climit=5)
3398 stats.pprint(top=10, file=sys.stderr, climit=5)
3432 else:
3399 else:
3433 return d()
3400 return d()
3434 finally:
3401 finally:
3435 u.flush()
3402 u.flush()
3436 except:
3403 except:
3437 # enter the debugger when we hit an exception
3404 # enter the debugger when we hit an exception
3438 if options['debugger']:
3405 if options['debugger']:
3439 pdb.post_mortem(sys.exc_info()[2])
3406 pdb.post_mortem(sys.exc_info()[2])
3440 u.print_exc()
3407 u.print_exc()
3441 raise
3408 raise
3442 except ParseError, inst:
3409 except ParseError, inst:
3443 if inst.args[0]:
3410 if inst.args[0]:
3444 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3411 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3445 help_(u, inst.args[0])
3412 help_(u, inst.args[0])
3446 else:
3413 else:
3447 u.warn(_("hg: %s\n") % inst.args[1])
3414 u.warn(_("hg: %s\n") % inst.args[1])
3448 help_(u, 'shortlist')
3415 help_(u, 'shortlist')
3449 except AmbiguousCommand, inst:
3416 except AmbiguousCommand, inst:
3450 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3417 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3451 (inst.args[0], " ".join(inst.args[1])))
3418 (inst.args[0], " ".join(inst.args[1])))
3452 except UnknownCommand, inst:
3419 except UnknownCommand, inst:
3453 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3420 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3454 help_(u, 'shortlist')
3421 help_(u, 'shortlist')
3455 except hg.RepoError, inst:
3422 except hg.RepoError, inst:
3456 u.warn(_("abort: %s!\n") % inst)
3423 u.warn(_("abort: %s!\n") % inst)
3457 except lock.LockHeld, inst:
3424 except lock.LockHeld, inst:
3458 if inst.errno == errno.ETIMEDOUT:
3425 if inst.errno == errno.ETIMEDOUT:
3459 reason = _('timed out waiting for lock held by %s') % inst.locker
3426 reason = _('timed out waiting for lock held by %s') % inst.locker
3460 else:
3427 else:
3461 reason = _('lock held by %s') % inst.locker
3428 reason = _('lock held by %s') % inst.locker
3462 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3429 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3463 except lock.LockUnavailable, inst:
3430 except lock.LockUnavailable, inst:
3464 u.warn(_("abort: could not lock %s: %s\n") %
3431 u.warn(_("abort: could not lock %s: %s\n") %
3465 (inst.desc or inst.filename, inst.strerror))
3432 (inst.desc or inst.filename, inst.strerror))
3466 except revlog.RevlogError, inst:
3433 except revlog.RevlogError, inst:
3467 u.warn(_("abort: "), inst, "!\n")
3434 u.warn(_("abort: "), inst, "!\n")
3468 except util.SignalInterrupt:
3435 except util.SignalInterrupt:
3469 u.warn(_("killed!\n"))
3436 u.warn(_("killed!\n"))
3470 except KeyboardInterrupt:
3437 except KeyboardInterrupt:
3471 try:
3438 try:
3472 u.warn(_("interrupted!\n"))
3439 u.warn(_("interrupted!\n"))
3473 except IOError, inst:
3440 except IOError, inst:
3474 if inst.errno == errno.EPIPE:
3441 if inst.errno == errno.EPIPE:
3475 if u.debugflag:
3442 if u.debugflag:
3476 u.warn(_("\nbroken pipe\n"))
3443 u.warn(_("\nbroken pipe\n"))
3477 else:
3444 else:
3478 raise
3445 raise
3479 except IOError, inst:
3446 except IOError, inst:
3480 if hasattr(inst, "code"):
3447 if hasattr(inst, "code"):
3481 u.warn(_("abort: %s\n") % inst)
3448 u.warn(_("abort: %s\n") % inst)
3482 elif hasattr(inst, "reason"):
3449 elif hasattr(inst, "reason"):
3483 u.warn(_("abort: error: %s\n") % inst.reason[1])
3450 u.warn(_("abort: error: %s\n") % inst.reason[1])
3484 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3451 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3485 if u.debugflag:
3452 if u.debugflag:
3486 u.warn(_("broken pipe\n"))
3453 u.warn(_("broken pipe\n"))
3487 elif getattr(inst, "strerror", None):
3454 elif getattr(inst, "strerror", None):
3488 if getattr(inst, "filename", None):
3455 if getattr(inst, "filename", None):
3489 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3456 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3490 else:
3457 else:
3491 u.warn(_("abort: %s\n") % inst.strerror)
3458 u.warn(_("abort: %s\n") % inst.strerror)
3492 else:
3459 else:
3493 raise
3460 raise
3494 except OSError, inst:
3461 except OSError, inst:
3495 if hasattr(inst, "filename"):
3462 if hasattr(inst, "filename"):
3496 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3463 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3497 else:
3464 else:
3498 u.warn(_("abort: %s\n") % inst.strerror)
3465 u.warn(_("abort: %s\n") % inst.strerror)
3499 except util.Abort, inst:
3466 except util.Abort, inst:
3500 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3467 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3501 except TypeError, inst:
3468 except TypeError, inst:
3502 # was this an argument error?
3469 # was this an argument error?
3503 tb = traceback.extract_tb(sys.exc_info()[2])
3470 tb = traceback.extract_tb(sys.exc_info()[2])
3504 if len(tb) > 2: # no
3471 if len(tb) > 2: # no
3505 raise
3472 raise
3506 u.debug(inst, "\n")
3473 u.debug(inst, "\n")
3507 u.warn(_("%s: invalid arguments\n") % cmd)
3474 u.warn(_("%s: invalid arguments\n") % cmd)
3508 help_(u, cmd)
3475 help_(u, cmd)
3509 except SystemExit, inst:
3476 except SystemExit, inst:
3510 # Commands shouldn't sys.exit directly, but give a return code.
3477 # Commands shouldn't sys.exit directly, but give a return code.
3511 # Just in case catch this and and pass exit code to caller.
3478 # Just in case catch this and and pass exit code to caller.
3512 return inst.code
3479 return inst.code
3513 except:
3480 except:
3514 u.warn(_("** unknown exception encountered, details follow\n"))
3481 u.warn(_("** unknown exception encountered, details follow\n"))
3515 u.warn(_("** report bug details to "
3482 u.warn(_("** report bug details to "
3516 "http://www.selenic.com/mercurial/bts\n"))
3483 "http://www.selenic.com/mercurial/bts\n"))
3517 u.warn(_("** or mercurial@selenic.com\n"))
3484 u.warn(_("** or mercurial@selenic.com\n"))
3518 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3485 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3519 % version.get_version())
3486 % version.get_version())
3520 raise
3487 raise
3521
3488
3522 return -1
3489 return -1
@@ -1,127 +1,155 b''
1 # filelog.py - file history class for mercurial
1 # filelog.py - file history class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from revlog import *
8 from revlog import *
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "bdiff os")
10 demandload(globals(), "bdiff os")
11
11
12 class filelog(revlog):
12 class filelog(revlog):
13 def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION):
13 def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION):
14 revlog.__init__(self, opener,
14 revlog.__init__(self, opener,
15 os.path.join("data", self.encodedir(path + ".i")),
15 os.path.join("data", self.encodedir(path + ".i")),
16 os.path.join("data", self.encodedir(path + ".d")),
16 os.path.join("data", self.encodedir(path + ".d")),
17 defversion)
17 defversion)
18
18
19 # This avoids a collision between a file named foo and a dir named
19 # This avoids a collision between a file named foo and a dir named
20 # foo.i or foo.d
20 # foo.i or foo.d
21 def encodedir(self, path):
21 def encodedir(self, path):
22 return (path
22 return (path
23 .replace(".hg/", ".hg.hg/")
23 .replace(".hg/", ".hg.hg/")
24 .replace(".i/", ".i.hg/")
24 .replace(".i/", ".i.hg/")
25 .replace(".d/", ".d.hg/"))
25 .replace(".d/", ".d.hg/"))
26
26
27 def decodedir(self, path):
27 def decodedir(self, path):
28 return (path
28 return (path
29 .replace(".d.hg/", ".d/")
29 .replace(".d.hg/", ".d/")
30 .replace(".i.hg/", ".i/")
30 .replace(".i.hg/", ".i/")
31 .replace(".hg.hg/", ".hg/"))
31 .replace(".hg.hg/", ".hg/"))
32
32
33 def read(self, node):
33 def read(self, node):
34 t = self.revision(node)
34 t = self.revision(node)
35 if not t.startswith('\1\n'):
35 if not t.startswith('\1\n'):
36 return t
36 return t
37 s = t.index('\1\n', 2)
37 s = t.index('\1\n', 2)
38 return t[s+2:]
38 return t[s+2:]
39
39
40 def readmeta(self, node):
40 def readmeta(self, node):
41 t = self.revision(node)
41 t = self.revision(node)
42 if not t.startswith('\1\n'):
42 if not t.startswith('\1\n'):
43 return {}
43 return {}
44 s = t.index('\1\n', 2)
44 s = t.index('\1\n', 2)
45 mt = t[2:s]
45 mt = t[2:s]
46 m = {}
46 m = {}
47 for l in mt.splitlines():
47 for l in mt.splitlines():
48 k, v = l.split(": ", 1)
48 k, v = l.split(": ", 1)
49 m[k] = v
49 m[k] = v
50 return m
50 return m
51
51
52 def add(self, text, meta, transaction, link, p1=None, p2=None):
52 def add(self, text, meta, transaction, link, p1=None, p2=None):
53 if meta or text.startswith('\1\n'):
53 if meta or text.startswith('\1\n'):
54 mt = ""
54 mt = ""
55 if meta:
55 if meta:
56 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
56 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
57 text = "\1\n%s\1\n%s" % ("".join(mt), text)
57 text = "\1\n%s\1\n%s" % ("".join(mt), text)
58 return self.addrevision(text, transaction, link, p1, p2)
58 return self.addrevision(text, transaction, link, p1, p2)
59
59
60 def renamed(self, node):
60 def renamed(self, node):
61 if self.parents(node)[0] != nullid:
61 if self.parents(node)[0] != nullid:
62 return False
62 return False
63 m = self.readmeta(node)
63 m = self.readmeta(node)
64 if m and m.has_key("copy"):
64 if m and m.has_key("copy"):
65 return (m["copy"], bin(m["copyrev"]))
65 return (m["copy"], bin(m["copyrev"]))
66 return False
66 return False
67
67
68 def size(self, rev):
68 def size(self, rev):
69 """return the size of a given revision"""
69 """return the size of a given revision"""
70
70
71 # for revisions with renames, we have to go the slow way
71 # for revisions with renames, we have to go the slow way
72 node = self.node(rev)
72 node = self.node(rev)
73 if self.renamed(node):
73 if self.renamed(node):
74 return len(self.read(node))
74 return len(self.read(node))
75
75
76 return revlog.size(self, rev)
76 return revlog.size(self, rev)
77
77
78 def cmp(self, node, text):
78 def cmp(self, node, text):
79 """compare text with a given file revision"""
79 """compare text with a given file revision"""
80
80
81 # for renames, we have to go the slow way
81 # for renames, we have to go the slow way
82 if self.renamed(node):
82 if self.renamed(node):
83 t2 = self.read(node)
83 t2 = self.read(node)
84 return t2 != text
84 return t2 != text
85
85
86 return revlog.cmp(self, node, text)
86 return revlog.cmp(self, node, text)
87
87
88 def annotate(self, node):
88 def annotate(self, node):
89
89
90 def decorate(text, rev):
90 def decorate(text, rev):
91 return ([rev] * len(text.splitlines()), text)
91 return ([rev] * len(text.splitlines()), text)
92
92
93 def pair(parent, child):
93 def pair(parent, child):
94 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
94 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
95 child[0][b1:b2] = parent[0][a1:a2]
95 child[0][b1:b2] = parent[0][a1:a2]
96 return child
96 return child
97
97
98 # find all ancestors
98 # find all ancestors
99 needed = {node:1}
99 needed = {(self, node):1}
100 visit = [node]
100 files = [self]
101 visit = [(self, node)]
101 while visit:
102 while visit:
102 n = visit.pop(0)
103 f, n = visit.pop(0)
103 for p in self.parents(n):
104 rn = f.renamed(n)
104 if p not in needed:
105 if rn:
105 needed[p] = 1
106 f, n = rn
106 visit.append(p)
107 f = filelog(self.opener, f, self.defversion)
108 files.insert(0, f)
109 if (f, n) not in needed:
110 needed[(f, n)] = 1
111 else:
112 needed[(f, n)] += 1
113 for p in f.parents(n):
114 if p == nullid:
115 continue
116 if (f, p) not in needed:
117 needed[(f, p)] = 1
118 visit.append((f, p))
107 else:
119 else:
108 # count how many times we'll use this
120 # count how many times we'll use this
109 needed[p] += 1
121 needed[(f, p)] += 1
110
122
111 # sort by revision which is a topological order
123 # sort by revision (per file) which is a topological order
112 visit = [ (self.rev(n), n) for n in needed.keys() ]
124 visit = []
113 visit.sort()
125 for f in files:
126 fn = [(f.rev(n[1]), f, n[1]) for n in needed.keys() if n[0] == f]
127 fn.sort()
128 visit.extend(fn)
114 hist = {}
129 hist = {}
115
130
116 for r,n in visit:
131 for i in range(len(visit)):
117 curr = decorate(self.read(n), self.linkrev(n))
132 r, f, n = visit[i]
118 for p in self.parents(n):
133 curr = decorate(f.read(n), f.linkrev(n))
134 if r == -1:
135 continue
136 parents = f.parents(n)
137 # follow parents across renames
138 if r < 1 and i > 0:
139 j = i
140 while j > 0 and visit[j][1] == f:
141 j -= 1
142 parents = (visit[j][2],)
143 f = visit[j][1]
144 else:
145 parents = f.parents(n)
146 for p in parents:
119 if p != nullid:
147 if p != nullid:
120 curr = pair(hist[p], curr)
148 curr = pair(hist[p], curr)
121 # trim the history of unneeded revs
149 # trim the history of unneeded revs
122 needed[p] -= 1
150 needed[(f, p)] -= 1
123 if not needed[p]:
151 if not needed[(f, p)]:
124 del hist[p]
152 del hist[p]
125 hist[n] = curr
153 hist[n] = curr
126
154
127 return zip(hist[n][0], hist[n][1].splitlines(1))
155 return zip(hist[n][0], hist[n][1].splitlines(1))
@@ -1,449 +1,539 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from node import *
10 from node import *
11 demandload(globals(), "cmdutil mdiff util")
11 demandload(globals(), "cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
12 demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
13
13
14 # helper functions
15
16 def copyfile(src, dst, basedir=None):
17 if not basedir:
18 basedir = os.getcwd()
19
20 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 if os.path.exists(absdst):
22 raise util.Abort(_("cannot create %s: destination already exists") %
23 dst)
24
25 targetdir = os.path.dirname(absdst)
26 if not os.path.isdir(targetdir):
27 os.makedirs(targetdir)
28 try:
29 shutil.copyfile(abssrc, absdst)
30 shutil.copymode(abssrc, absdst)
31 except shutil.Error, inst:
32 raise util.Abort(str(inst))
33
34 # public functions
35
14 def extract(ui, fileobj):
36 def extract(ui, fileobj):
15 '''extract patch from data read from fileobj.
37 '''extract patch from data read from fileobj.
16
38
17 patch can be normal patch or contained in email message.
39 patch can be normal patch or contained in email message.
18
40
19 return tuple (filename, message, user, date). any item in returned
41 return tuple (filename, message, user, date). any item in returned
20 tuple can be None. if filename is None, fileobj did not contain
42 tuple can be None. if filename is None, fileobj did not contain
21 patch. caller must unlink filename when done.'''
43 patch. caller must unlink filename when done.'''
22
44
23 # attempt to detect the start of a patch
45 # attempt to detect the start of a patch
24 # (this heuristic is borrowed from quilt)
46 # (this heuristic is borrowed from quilt)
25 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
47 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
26 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
48 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
27 '(---|\*\*\*)[ \t])', re.MULTILINE)
49 '(---|\*\*\*)[ \t])', re.MULTILINE)
28
50
29 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
51 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
30 tmpfp = os.fdopen(fd, 'w')
52 tmpfp = os.fdopen(fd, 'w')
31 try:
53 try:
32 hgpatch = False
54 hgpatch = False
33
55
34 msg = email.Parser.Parser().parse(fileobj)
56 msg = email.Parser.Parser().parse(fileobj)
35
57
36 message = msg['Subject']
58 message = msg['Subject']
37 user = msg['From']
59 user = msg['From']
38 # should try to parse msg['Date']
60 # should try to parse msg['Date']
39 date = None
61 date = None
40
62
41 if message:
63 if message:
42 message = message.replace('\n\t', ' ')
64 message = message.replace('\n\t', ' ')
43 ui.debug('Subject: %s\n' % message)
65 ui.debug('Subject: %s\n' % message)
44 if user:
66 if user:
45 ui.debug('From: %s\n' % user)
67 ui.debug('From: %s\n' % user)
46 diffs_seen = 0
68 diffs_seen = 0
47 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
69 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
48
70
49 for part in msg.walk():
71 for part in msg.walk():
50 content_type = part.get_content_type()
72 content_type = part.get_content_type()
51 ui.debug('Content-Type: %s\n' % content_type)
73 ui.debug('Content-Type: %s\n' % content_type)
52 if content_type not in ok_types:
74 if content_type not in ok_types:
53 continue
75 continue
54 payload = part.get_payload(decode=True)
76 payload = part.get_payload(decode=True)
55 m = diffre.search(payload)
77 m = diffre.search(payload)
56 if m:
78 if m:
57 ui.debug(_('found patch at byte %d\n') % m.start(0))
79 ui.debug(_('found patch at byte %d\n') % m.start(0))
58 diffs_seen += 1
80 diffs_seen += 1
59 cfp = cStringIO.StringIO()
81 cfp = cStringIO.StringIO()
60 if message:
82 if message:
61 cfp.write(message)
83 cfp.write(message)
62 cfp.write('\n')
84 cfp.write('\n')
63 for line in payload[:m.start(0)].splitlines():
85 for line in payload[:m.start(0)].splitlines():
64 if line.startswith('# HG changeset patch'):
86 if line.startswith('# HG changeset patch'):
65 ui.debug(_('patch generated by hg export\n'))
87 ui.debug(_('patch generated by hg export\n'))
66 hgpatch = True
88 hgpatch = True
67 # drop earlier commit message content
89 # drop earlier commit message content
68 cfp.seek(0)
90 cfp.seek(0)
69 cfp.truncate()
91 cfp.truncate()
70 elif hgpatch:
92 elif hgpatch:
71 if line.startswith('# User '):
93 if line.startswith('# User '):
72 user = line[7:]
94 user = line[7:]
73 ui.debug('From: %s\n' % user)
95 ui.debug('From: %s\n' % user)
74 elif line.startswith("# Date "):
96 elif line.startswith("# Date "):
75 date = line[7:]
97 date = line[7:]
76 if not line.startswith('# '):
98 if not line.startswith('# '):
77 cfp.write(line)
99 cfp.write(line)
78 cfp.write('\n')
100 cfp.write('\n')
79 message = cfp.getvalue()
101 message = cfp.getvalue()
80 if tmpfp:
102 if tmpfp:
81 tmpfp.write(payload)
103 tmpfp.write(payload)
82 if not payload.endswith('\n'):
104 if not payload.endswith('\n'):
83 tmpfp.write('\n')
105 tmpfp.write('\n')
84 elif not diffs_seen and message and content_type == 'text/plain':
106 elif not diffs_seen and message and content_type == 'text/plain':
85 message += '\n' + payload
107 message += '\n' + payload
86 except:
108 except:
87 tmpfp.close()
109 tmpfp.close()
88 os.unlink(tmpname)
110 os.unlink(tmpname)
89 raise
111 raise
90
112
91 tmpfp.close()
113 tmpfp.close()
92 if not diffs_seen:
114 if not diffs_seen:
93 os.unlink(tmpname)
115 os.unlink(tmpname)
94 return None, message, user, date
116 return None, message, user, date
95 return tmpname, message, user, date
117 return tmpname, message, user, date
96
118
97 def readgitpatch(patchname):
119 def readgitpatch(patchname):
98 """extract git-style metadata about patches from <patchname>"""
120 """extract git-style metadata about patches from <patchname>"""
99 class gitpatch:
121 class gitpatch:
100 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
122 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
101 def __init__(self, path):
123 def __init__(self, path):
102 self.path = path
124 self.path = path
103 self.oldpath = None
125 self.oldpath = None
104 self.mode = None
126 self.mode = None
105 self.op = 'MODIFY'
127 self.op = 'MODIFY'
106 self.copymod = False
128 self.copymod = False
107 self.lineno = 0
129 self.lineno = 0
108
130
109 # Filter patch for git information
131 # Filter patch for git information
110 gitre = re.compile('diff --git a/(.*) b/(.*)')
132 gitre = re.compile('diff --git a/(.*) b/(.*)')
111 pf = file(patchname)
133 pf = file(patchname)
112 gp = None
134 gp = None
113 gitpatches = []
135 gitpatches = []
114 # Can have a git patch with only metadata, causing patch to complain
136 # Can have a git patch with only metadata, causing patch to complain
115 dopatch = False
137 dopatch = False
116
138
117 lineno = 0
139 lineno = 0
118 for line in pf:
140 for line in pf:
119 lineno += 1
141 lineno += 1
120 if line.startswith('diff --git'):
142 if line.startswith('diff --git'):
121 m = gitre.match(line)
143 m = gitre.match(line)
122 if m:
144 if m:
123 if gp:
145 if gp:
124 gitpatches.append(gp)
146 gitpatches.append(gp)
125 src, dst = m.group(1,2)
147 src, dst = m.group(1,2)
126 gp = gitpatch(dst)
148 gp = gitpatch(dst)
127 gp.lineno = lineno
149 gp.lineno = lineno
128 elif gp:
150 elif gp:
129 if line.startswith('--- '):
151 if line.startswith('--- '):
130 if gp.op in ('COPY', 'RENAME'):
152 if gp.op in ('COPY', 'RENAME'):
131 gp.copymod = True
153 gp.copymod = True
132 dopatch = 'filter'
154 dopatch = 'filter'
133 gitpatches.append(gp)
155 gitpatches.append(gp)
134 gp = None
156 gp = None
135 if not dopatch:
157 if not dopatch:
136 dopatch = True
158 dopatch = True
137 continue
159 continue
138 if line.startswith('rename from '):
160 if line.startswith('rename from '):
139 gp.op = 'RENAME'
161 gp.op = 'RENAME'
140 gp.oldpath = line[12:].rstrip()
162 gp.oldpath = line[12:].rstrip()
141 elif line.startswith('rename to '):
163 elif line.startswith('rename to '):
142 gp.path = line[10:].rstrip()
164 gp.path = line[10:].rstrip()
143 elif line.startswith('copy from '):
165 elif line.startswith('copy from '):
144 gp.op = 'COPY'
166 gp.op = 'COPY'
145 gp.oldpath = line[10:].rstrip()
167 gp.oldpath = line[10:].rstrip()
146 elif line.startswith('copy to '):
168 elif line.startswith('copy to '):
147 gp.path = line[8:].rstrip()
169 gp.path = line[8:].rstrip()
148 elif line.startswith('deleted file'):
170 elif line.startswith('deleted file'):
149 gp.op = 'DELETE'
171 gp.op = 'DELETE'
150 elif line.startswith('new file mode '):
172 elif line.startswith('new file mode '):
151 gp.op = 'ADD'
173 gp.op = 'ADD'
152 gp.mode = int(line.rstrip()[-3:], 8)
174 gp.mode = int(line.rstrip()[-3:], 8)
153 elif line.startswith('new mode '):
175 elif line.startswith('new mode '):
154 gp.mode = int(line.rstrip()[-3:], 8)
176 gp.mode = int(line.rstrip()[-3:], 8)
155 if gp:
177 if gp:
156 gitpatches.append(gp)
178 gitpatches.append(gp)
157
179
158 if not gitpatches:
180 if not gitpatches:
159 dopatch = True
181 dopatch = True
160
182
161 return (dopatch, gitpatches)
183 return (dopatch, gitpatches)
162
184
163 def dogitpatch(patchname, gitpatches):
185 def dogitpatch(patchname, gitpatches):
164 """Preprocess git patch so that vanilla patch can handle it"""
186 """Preprocess git patch so that vanilla patch can handle it"""
165 pf = file(patchname)
187 pf = file(patchname)
166 pfline = 1
188 pfline = 1
167
189
168 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
190 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
169 tmpfp = os.fdopen(fd, 'w')
191 tmpfp = os.fdopen(fd, 'w')
170
192
171 try:
193 try:
172 for i in range(len(gitpatches)):
194 for i in range(len(gitpatches)):
173 p = gitpatches[i]
195 p = gitpatches[i]
174 if not p.copymod:
196 if not p.copymod:
175 continue
197 continue
176
198
177 if os.path.exists(p.path):
199 copyfile(p.oldpath, p.path)
178 raise util.Abort(_("cannot create %s: destination already exists") %
179 p.path)
180
181 (src, dst) = [os.path.join(os.getcwd(), n)
182 for n in (p.oldpath, p.path)]
183
184 targetdir = os.path.dirname(dst)
185 if not os.path.isdir(targetdir):
186 os.makedirs(targetdir)
187 try:
188 shutil.copyfile(src, dst)
189 shutil.copymode(src, dst)
190 except shutil.Error, inst:
191 raise util.Abort(str(inst))
192
200
193 # rewrite patch hunk
201 # rewrite patch hunk
194 while pfline < p.lineno:
202 while pfline < p.lineno:
195 tmpfp.write(pf.readline())
203 tmpfp.write(pf.readline())
196 pfline += 1
204 pfline += 1
197 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
205 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
198 line = pf.readline()
206 line = pf.readline()
199 pfline += 1
207 pfline += 1
200 while not line.startswith('--- a/'):
208 while not line.startswith('--- a/'):
201 tmpfp.write(line)
209 tmpfp.write(line)
202 line = pf.readline()
210 line = pf.readline()
203 pfline += 1
211 pfline += 1
204 tmpfp.write('--- a/%s\n' % p.path)
212 tmpfp.write('--- a/%s\n' % p.path)
205
213
206 line = pf.readline()
214 line = pf.readline()
207 while line:
215 while line:
208 tmpfp.write(line)
216 tmpfp.write(line)
209 line = pf.readline()
217 line = pf.readline()
210 except:
218 except:
211 tmpfp.close()
219 tmpfp.close()
212 os.unlink(patchname)
220 os.unlink(patchname)
213 raise
221 raise
214
222
215 tmpfp.close()
223 tmpfp.close()
216 return patchname
224 return patchname
217
225
218 def patch(patchname, ui, strip=1, cwd=None):
226 def patch(patchname, ui, strip=1, cwd=None):
219 """apply the patch <patchname> to the working directory.
227 """apply the patch <patchname> to the working directory.
220 a list of patched files is returned"""
228 a list of patched files is returned"""
221
229
222 (dopatch, gitpatches) = readgitpatch(patchname)
230 (dopatch, gitpatches) = readgitpatch(patchname)
223
231
224 files = {}
232 files = {}
225 fuzz = False
233 fuzz = False
226 if dopatch:
234 if dopatch:
227 if dopatch == 'filter':
235 if dopatch == 'filter':
228 patchname = dogitpatch(patchname, gitpatches)
236 patchname = dogitpatch(patchname, gitpatches)
229 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
237 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
230 args = []
238 args = []
231 if cwd:
239 if cwd:
232 args.append('-d %s' % util.shellquote(cwd))
240 args.append('-d %s' % util.shellquote(cwd))
233 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
241 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
234 util.shellquote(patchname)))
242 util.shellquote(patchname)))
235
243
236 if dopatch == 'filter':
244 if dopatch == 'filter':
237 False and os.unlink(patchname)
245 False and os.unlink(patchname)
238
246
239 for line in fp:
247 for line in fp:
240 line = line.rstrip()
248 line = line.rstrip()
241 ui.note(line + '\n')
249 ui.note(line + '\n')
242 if line.startswith('patching file '):
250 if line.startswith('patching file '):
243 pf = util.parse_patch_output(line)
251 pf = util.parse_patch_output(line)
244 printed_file = False
252 printed_file = False
245 files.setdefault(pf, (None, None))
253 files.setdefault(pf, (None, None))
246 elif line.find('with fuzz') >= 0:
254 elif line.find('with fuzz') >= 0:
247 fuzz = True
255 fuzz = True
248 if not printed_file:
256 if not printed_file:
249 ui.warn(pf + '\n')
257 ui.warn(pf + '\n')
250 printed_file = True
258 printed_file = True
251 ui.warn(line + '\n')
259 ui.warn(line + '\n')
252 elif line.find('saving rejects to file') >= 0:
260 elif line.find('saving rejects to file') >= 0:
253 ui.warn(line + '\n')
261 ui.warn(line + '\n')
254 elif line.find('FAILED') >= 0:
262 elif line.find('FAILED') >= 0:
255 if not printed_file:
263 if not printed_file:
256 ui.warn(pf + '\n')
264 ui.warn(pf + '\n')
257 printed_file = True
265 printed_file = True
258 ui.warn(line + '\n')
266 ui.warn(line + '\n')
259
267
260 code = fp.close()
268 code = fp.close()
261 if code:
269 if code:
262 raise util.Abort(_("patch command failed: %s") %
270 raise util.Abort(_("patch command failed: %s") %
263 util.explain_exit(code)[0])
271 util.explain_exit(code)[0])
264
272
265 for gp in gitpatches:
273 for gp in gitpatches:
266 files[gp.path] = (gp.op, gp)
274 files[gp.path] = (gp.op, gp)
267
275
268 return (files, fuzz)
276 return (files, fuzz)
269
277
270 def diffopts(ui, opts={}):
278 def diffopts(ui, opts={}):
271 return mdiff.diffopts(
279 return mdiff.diffopts(
272 text=opts.get('text'),
280 text=opts.get('text'),
273 git=(opts.get('git') or
281 git=(opts.get('git') or
274 ui.configbool('diff', 'git', None)),
282 ui.configbool('diff', 'git', None)),
275 showfunc=(opts.get('show_function') or
283 showfunc=(opts.get('show_function') or
276 ui.configbool('diff', 'showfunc', None)),
284 ui.configbool('diff', 'showfunc', None)),
277 ignorews=(opts.get('ignore_all_space') or
285 ignorews=(opts.get('ignore_all_space') or
278 ui.configbool('diff', 'ignorews', None)),
286 ui.configbool('diff', 'ignorews', None)),
279 ignorewsamount=(opts.get('ignore_space_change') or
287 ignorewsamount=(opts.get('ignore_space_change') or
280 ui.configbool('diff', 'ignorewsamount', None)),
288 ui.configbool('diff', 'ignorewsamount', None)),
281 ignoreblanklines=(opts.get('ignore_blank_lines') or
289 ignoreblanklines=(opts.get('ignore_blank_lines') or
282 ui.configbool('diff', 'ignoreblanklines', None)))
290 ui.configbool('diff', 'ignoreblanklines', None)))
283
291
292 def updatedir(ui, repo, patches, wlock=None):
293 '''Update dirstate after patch application according to metadata'''
294 if not patches:
295 return
296 copies = []
297 removes = []
298 cfiles = patches.keys()
299 copts = {'after': False, 'force': False}
300 cwd = repo.getcwd()
301 if cwd:
302 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
303 for f in patches:
304 ctype, gp = patches[f]
305 if ctype == 'RENAME':
306 copies.append((gp.oldpath, gp.path, gp.copymod))
307 removes.append(gp.oldpath)
308 elif ctype == 'COPY':
309 copies.append((gp.oldpath, gp.path, gp.copymod))
310 elif ctype == 'DELETE':
311 removes.append(gp.path)
312 for src, dst, after in copies:
313 if not after:
314 copyfile(src, dst, repo.root)
315 repo.copy(src, dst, wlock=wlock)
316 if removes:
317 repo.remove(removes, True, wlock=wlock)
318 for f in patches:
319 ctype, gp = patches[f]
320 if gp and gp.mode:
321 x = gp.mode & 0100 != 0
322 dst = os.path.join(repo.root, gp.path)
323 util.set_exec(dst, x)
324 cmdutil.addremove(repo, cfiles, wlock=wlock)
325 files = patches.keys()
326 files.extend([r for r in removes if r not in files])
327 files.sort()
328
329 return files
330
284 def diff(repo, node1=None, node2=None, files=None, match=util.always,
331 def diff(repo, node1=None, node2=None, files=None, match=util.always,
285 fp=None, changes=None, opts=None):
332 fp=None, changes=None, opts=None):
286 '''print diff of changes to files between two nodes, or node and
333 '''print diff of changes to files between two nodes, or node and
287 working directory.
334 working directory.
288
335
289 if node1 is None, use first dirstate parent instead.
336 if node1 is None, use first dirstate parent instead.
290 if node2 is None, compare node1 with working directory.'''
337 if node2 is None, compare node1 with working directory.'''
291
338
292 if opts is None:
339 if opts is None:
293 opts = mdiff.defaultopts
340 opts = mdiff.defaultopts
294 if fp is None:
341 if fp is None:
295 fp = repo.ui
342 fp = repo.ui
296
343
297 if not node1:
344 if not node1:
298 node1 = repo.dirstate.parents()[0]
345 node1 = repo.dirstate.parents()[0]
346
347 clcache = {}
348 def getchangelog(n):
349 if n not in clcache:
350 clcache[n] = repo.changelog.read(n)
351 return clcache[n]
352 mcache = {}
353 def getmanifest(n):
354 if n not in mcache:
355 mcache[n] = repo.manifest.read(n)
356 return mcache[n]
357 fcache = {}
358 def getfile(f):
359 if f not in fcache:
360 fcache[f] = repo.file(f)
361 return fcache[f]
362
299 # reading the data for node1 early allows it to play nicely
363 # reading the data for node1 early allows it to play nicely
300 # with repo.status and the revlog cache.
364 # with repo.status and the revlog cache.
301 change = repo.changelog.read(node1)
365 change = getchangelog(node1)
302 mmap = repo.manifest.read(change[0])
366 mmap = getmanifest(change[0])
303 date1 = util.datestr(change[2])
367 date1 = util.datestr(change[2])
304
368
305 if not changes:
369 if not changes:
306 changes = repo.status(node1, node2, files, match=match)[:5]
370 changes = repo.status(node1, node2, files, match=match)[:5]
307 modified, added, removed, deleted, unknown = changes
371 modified, added, removed, deleted, unknown = changes
308 if files:
372 if files:
309 def filterfiles(filters):
373 def filterfiles(filters):
310 l = [x for x in filters if x in files]
374 l = [x for x in filters if x in files]
311
375
312 for t in files:
376 for t in files:
313 if not t.endswith("/"):
377 if not t.endswith("/"):
314 t += "/"
378 t += "/"
315 l += [x for x in filters if x.startswith(t)]
379 l += [x for x in filters if x.startswith(t)]
316 return l
380 return l
317
381
318 modified, added, removed = map(filterfiles, (modified, added, removed))
382 modified, added, removed = map(filterfiles, (modified, added, removed))
319
383
320 if not modified and not added and not removed:
384 if not modified and not added and not removed:
321 return
385 return
322
386
387 def renamedbetween(f, n1, n2):
388 r1, r2 = map(repo.changelog.rev, (n1, n2))
389 src = None
390 while r2 > r1:
391 cl = getchangelog(n2)[0]
392 m = getmanifest(cl)
393 try:
394 src = getfile(f).renamed(m[f])
395 except KeyError:
396 return None
397 if src:
398 f = src[0]
399 n2 = repo.changelog.parents(n2)[0]
400 r2 = repo.changelog.rev(n2)
401 return src
402
323 if node2:
403 if node2:
324 change = repo.changelog.read(node2)
404 change = getchangelog(node2)
325 mmap2 = repo.manifest.read(change[0])
405 mmap2 = getmanifest(change[0])
326 _date2 = util.datestr(change[2])
406 _date2 = util.datestr(change[2])
327 def date2(f):
407 def date2(f):
328 return _date2
408 return _date2
329 def read(f):
409 def read(f):
330 return repo.file(f).read(mmap2[f])
410 return getfile(f).read(mmap2[f])
331 def renamed(f):
411 def renamed(f):
332 src = repo.file(f).renamed(mmap2[f])
412 return renamedbetween(f, node1, node2)
333 return src and src[0] or None
334 else:
413 else:
335 tz = util.makedate()[1]
414 tz = util.makedate()[1]
336 _date2 = util.datestr()
415 _date2 = util.datestr()
337 def date2(f):
416 def date2(f):
338 try:
417 try:
339 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
418 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
340 except OSError, err:
419 except OSError, err:
341 if err.errno != errno.ENOENT: raise
420 if err.errno != errno.ENOENT: raise
342 return _date2
421 return _date2
343 def read(f):
422 def read(f):
344 return repo.wread(f)
423 return repo.wread(f)
345 def renamed(f):
424 def renamed(f):
346 return repo.dirstate.copies.get(f)
425 src = repo.dirstate.copies.get(f)
426 parent = repo.dirstate.parents()[0]
427 if src:
428 f = src[0]
429 of = renamedbetween(f, node1, parent)
430 if of:
431 return of
432 elif src:
433 cl = getchangelog(parent)[0]
434 return (src, getmanifest(cl)[src])
435 else:
436 return None
347
437
348 if repo.ui.quiet:
438 if repo.ui.quiet:
349 r = None
439 r = None
350 else:
440 else:
351 hexfunc = repo.ui.verbose and hex or short
441 hexfunc = repo.ui.verbose and hex or short
352 r = [hexfunc(node) for node in [node1, node2] if node]
442 r = [hexfunc(node) for node in [node1, node2] if node]
353
443
354 if opts.git:
444 if opts.git:
355 copied = {}
445 copied = {}
356 for f in added:
446 for f in added:
357 src = renamed(f)
447 src = renamed(f)
358 if src:
448 if src:
359 copied[f] = src
449 copied[f] = src
360 srcs = [x[1] for x in copied.items()]
450 srcs = [x[1][0] for x in copied.items()]
361
451
362 all = modified + added + removed
452 all = modified + added + removed
363 all.sort()
453 all.sort()
364 for f in all:
454 for f in all:
365 to = None
455 to = None
366 tn = None
456 tn = None
367 dodiff = True
457 dodiff = True
368 if f in mmap:
458 if f in mmap:
369 to = repo.file(f).read(mmap[f])
459 to = getfile(f).read(mmap[f])
370 if f not in removed:
460 if f not in removed:
371 tn = read(f)
461 tn = read(f)
372 if opts.git:
462 if opts.git:
373 def gitmode(x):
463 def gitmode(x):
374 return x and '100755' or '100644'
464 return x and '100755' or '100644'
375 def addmodehdr(header, omode, nmode):
465 def addmodehdr(header, omode, nmode):
376 if omode != nmode:
466 if omode != nmode:
377 header.append('old mode %s\n' % omode)
467 header.append('old mode %s\n' % omode)
378 header.append('new mode %s\n' % nmode)
468 header.append('new mode %s\n' % nmode)
379
469
380 a, b = f, f
470 a, b = f, f
381 header = []
471 header = []
382 if f in added:
472 if f in added:
383 if node2:
473 if node2:
384 mode = gitmode(mmap2.execf(f))
474 mode = gitmode(mmap2.execf(f))
385 else:
475 else:
386 mode = gitmode(util.is_exec(repo.wjoin(f), None))
476 mode = gitmode(util.is_exec(repo.wjoin(f), None))
387 if f in copied:
477 if f in copied:
388 a = copied[f]
478 a, arev = copied[f]
389 omode = gitmode(mmap.execf(a))
479 omode = gitmode(mmap.execf(a))
390 addmodehdr(header, omode, mode)
480 addmodehdr(header, omode, mode)
391 op = a in removed and 'rename' or 'copy'
481 op = a in removed and 'rename' or 'copy'
392 header.append('%s from %s\n' % (op, a))
482 header.append('%s from %s\n' % (op, a))
393 header.append('%s to %s\n' % (op, f))
483 header.append('%s to %s\n' % (op, f))
394 to = repo.file(a).read(mmap[a])
484 to = getfile(a).read(arev)
395 else:
485 else:
396 header.append('new file mode %s\n' % mode)
486 header.append('new file mode %s\n' % mode)
397 elif f in removed:
487 elif f in removed:
398 if f in srcs:
488 if f in srcs:
399 dodiff = False
489 dodiff = False
400 else:
490 else:
401 mode = gitmode(mmap.execf(f))
491 mode = gitmode(mmap.execf(f))
402 header.append('deleted file mode %s\n' % mode)
492 header.append('deleted file mode %s\n' % mode)
403 else:
493 else:
404 omode = gitmode(mmap.execf(f))
494 omode = gitmode(mmap.execf(f))
405 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
495 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
406 addmodehdr(header, omode, nmode)
496 addmodehdr(header, omode, nmode)
407 r = None
497 r = None
408 if dodiff:
498 if dodiff:
409 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
499 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
410 fp.write(''.join(header))
500 fp.write(''.join(header))
411 if dodiff:
501 if dodiff:
412 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
502 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
413
503
414 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
504 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
415 opts=None):
505 opts=None):
416 '''export changesets as hg patches.'''
506 '''export changesets as hg patches.'''
417
507
418 total = len(revs)
508 total = len(revs)
419 revwidth = max(map(len, revs))
509 revwidth = max(map(len, revs))
420
510
421 def single(node, seqno, fp):
511 def single(node, seqno, fp):
422 parents = [p for p in repo.changelog.parents(node) if p != nullid]
512 parents = [p for p in repo.changelog.parents(node) if p != nullid]
423 if switch_parent:
513 if switch_parent:
424 parents.reverse()
514 parents.reverse()
425 prev = (parents and parents[0]) or nullid
515 prev = (parents and parents[0]) or nullid
426 change = repo.changelog.read(node)
516 change = repo.changelog.read(node)
427
517
428 if not fp:
518 if not fp:
429 fp = cmdutil.make_file(repo, template, node, total=total,
519 fp = cmdutil.make_file(repo, template, node, total=total,
430 seqno=seqno, revwidth=revwidth)
520 seqno=seqno, revwidth=revwidth)
431 if fp not in (sys.stdout, repo.ui):
521 if fp not in (sys.stdout, repo.ui):
432 repo.ui.note("%s\n" % fp.name)
522 repo.ui.note("%s\n" % fp.name)
433
523
434 fp.write("# HG changeset patch\n")
524 fp.write("# HG changeset patch\n")
435 fp.write("# User %s\n" % change[1])
525 fp.write("# User %s\n" % change[1])
436 fp.write("# Date %d %d\n" % change[2])
526 fp.write("# Date %d %d\n" % change[2])
437 fp.write("# Node ID %s\n" % hex(node))
527 fp.write("# Node ID %s\n" % hex(node))
438 fp.write("# Parent %s\n" % hex(prev))
528 fp.write("# Parent %s\n" % hex(prev))
439 if len(parents) > 1:
529 if len(parents) > 1:
440 fp.write("# Parent %s\n" % hex(parents[1]))
530 fp.write("# Parent %s\n" % hex(parents[1]))
441 fp.write(change[4].rstrip())
531 fp.write(change[4].rstrip())
442 fp.write("\n\n")
532 fp.write("\n\n")
443
533
444 diff(repo, prev, node, fp=fp, opts=opts)
534 diff(repo, prev, node, fp=fp, opts=opts)
445 if fp not in (sys.stdout, repo.ui):
535 if fp not in (sys.stdout, repo.ui):
446 fp.close()
536 fp.close()
447
537
448 for seqno, cset in enumerate(revs):
538 for seqno, cset in enumerate(revs):
449 single(cset, seqno, fp)
539 single(cset, seqno, fp)
@@ -1,290 +1,295 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import gettext as _
8 from i18n import gettext as _
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "errno getpass os re socket sys tempfile")
10 demandload(globals(), "errno getpass os re socket sys tempfile")
11 demandload(globals(), "ConfigParser mdiff templater traceback util")
11 demandload(globals(), "ConfigParser mdiff templater traceback 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, traceback=False, parentui=None):
15 interactive=True, traceback=False, parentui=None,
16 readhooks=[]):
16 self.overlay = {}
17 self.overlay = {}
17 if parentui is None:
18 if parentui is None:
18 # this is the parent of all ui children
19 # this is the parent of all ui children
19 self.parentui = None
20 self.parentui = None
21 self.readhooks = list(readhooks)
20 self.cdata = ConfigParser.SafeConfigParser()
22 self.cdata = ConfigParser.SafeConfigParser()
21 self.readconfig(util.rcpath())
23 self.readconfig(util.rcpath())
22
24
23 self.quiet = self.configbool("ui", "quiet")
25 self.quiet = self.configbool("ui", "quiet")
24 self.verbose = self.configbool("ui", "verbose")
26 self.verbose = self.configbool("ui", "verbose")
25 self.debugflag = self.configbool("ui", "debug")
27 self.debugflag = self.configbool("ui", "debug")
26 self.interactive = self.configbool("ui", "interactive", True)
28 self.interactive = self.configbool("ui", "interactive", True)
27 self.traceback = traceback
29 self.traceback = traceback
28
30
29 self.updateopts(verbose, debug, quiet, interactive)
31 self.updateopts(verbose, debug, quiet, interactive)
30 self.diffcache = None
32 self.diffcache = None
31 self.header = []
33 self.header = []
32 self.prev_header = []
34 self.prev_header = []
33 self.revlogopts = self.configrevlog()
35 self.revlogopts = self.configrevlog()
34 else:
36 else:
35 # parentui may point to an ui object which is already a child
37 # parentui may point to an ui object which is already a child
36 self.parentui = parentui.parentui or parentui
38 self.parentui = parentui.parentui or parentui
39 self.readhooks = list(parentui.readhooks or readhooks)
37 parent_cdata = self.parentui.cdata
40 parent_cdata = self.parentui.cdata
38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
41 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 # make interpolation work
42 # make interpolation work
40 for section in parent_cdata.sections():
43 for section in parent_cdata.sections():
41 self.cdata.add_section(section)
44 self.cdata.add_section(section)
42 for name, value in parent_cdata.items(section, raw=True):
45 for name, value in parent_cdata.items(section, raw=True):
43 self.cdata.set(section, name, value)
46 self.cdata.set(section, name, value)
44
47
45 def __getattr__(self, key):
48 def __getattr__(self, key):
46 return getattr(self.parentui, key)
49 return getattr(self.parentui, key)
47
50
48 def updateopts(self, verbose=False, debug=False, quiet=False,
51 def updateopts(self, verbose=False, debug=False, quiet=False,
49 interactive=True, traceback=False, config=[]):
52 interactive=True, traceback=False, config=[]):
50 self.quiet = (self.quiet or quiet) and not verbose and not debug
53 self.quiet = (self.quiet or quiet) and not verbose and not debug
51 self.verbose = (self.verbose or verbose) or debug
54 self.verbose = (self.verbose or verbose) or debug
52 self.debugflag = (self.debugflag or debug)
55 self.debugflag = (self.debugflag or debug)
53 self.interactive = (self.interactive and interactive)
56 self.interactive = (self.interactive and interactive)
54 self.traceback = self.traceback or traceback
57 self.traceback = self.traceback or traceback
55 for cfg in config:
58 for cfg in config:
56 try:
59 try:
57 name, value = cfg.split('=', 1)
60 name, value = cfg.split('=', 1)
58 section, name = name.split('.', 1)
61 section, name = name.split('.', 1)
59 if not self.cdata.has_section(section):
62 if not self.cdata.has_section(section):
60 self.cdata.add_section(section)
63 self.cdata.add_section(section)
61 if not section or not name:
64 if not section or not name:
62 raise IndexError
65 raise IndexError
63 self.cdata.set(section, name, value)
66 self.cdata.set(section, name, value)
64 except (IndexError, ValueError):
67 except (IndexError, ValueError):
65 raise util.Abort(_('malformed --config option: %s') % cfg)
68 raise util.Abort(_('malformed --config option: %s') % cfg)
66
69
67 def readconfig(self, fn, root=None):
70 def readconfig(self, fn, root=None):
68 if isinstance(fn, basestring):
71 if isinstance(fn, basestring):
69 fn = [fn]
72 fn = [fn]
70 for f in fn:
73 for f in fn:
71 try:
74 try:
72 self.cdata.read(f)
75 self.cdata.read(f)
73 except ConfigParser.ParsingError, inst:
76 except ConfigParser.ParsingError, inst:
74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
77 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
75 # translate paths relative to root (or home) into absolute paths
78 # translate paths relative to root (or home) into absolute paths
76 if root is None:
79 if root is None:
77 root = os.path.expanduser('~')
80 root = os.path.expanduser('~')
78 for name, path in self.configitems("paths"):
81 for name, path in self.configitems("paths"):
79 if path and "://" not in path and not os.path.isabs(path):
82 if path and "://" not in path and not os.path.isabs(path):
80 self.cdata.set("paths", name, os.path.join(root, path))
83 self.cdata.set("paths", name, os.path.join(root, path))
84 for hook in self.readhooks:
85 hook(self)
81
86
82 def setconfig(self, section, name, val):
87 def setconfig(self, section, name, val):
83 self.overlay[(section, name)] = val
88 self.overlay[(section, name)] = val
84
89
85 def config(self, section, name, default=None):
90 def config(self, section, name, default=None):
86 if self.overlay.has_key((section, name)):
91 if self.overlay.has_key((section, name)):
87 return self.overlay[(section, name)]
92 return self.overlay[(section, name)]
88 if self.cdata.has_option(section, name):
93 if self.cdata.has_option(section, name):
89 try:
94 try:
90 return self.cdata.get(section, name)
95 return self.cdata.get(section, name)
91 except ConfigParser.InterpolationError, inst:
96 except ConfigParser.InterpolationError, inst:
92 raise util.Abort(_("Error in configuration:\n%s") % inst)
97 raise util.Abort(_("Error in configuration:\n%s") % inst)
93 if self.parentui is None:
98 if self.parentui is None:
94 return default
99 return default
95 else:
100 else:
96 return self.parentui.config(section, name, default)
101 return self.parentui.config(section, name, default)
97
102
98 def configlist(self, section, name, default=None):
103 def configlist(self, section, name, default=None):
99 """Return a list of comma/space separated strings"""
104 """Return a list of comma/space separated strings"""
100 result = self.config(section, name)
105 result = self.config(section, name)
101 if result is None:
106 if result is None:
102 result = default or []
107 result = default or []
103 if isinstance(result, basestring):
108 if isinstance(result, basestring):
104 result = result.replace(",", " ").split()
109 result = result.replace(",", " ").split()
105 return result
110 return result
106
111
107 def configbool(self, section, name, default=False):
112 def configbool(self, section, name, default=False):
108 if self.overlay.has_key((section, name)):
113 if self.overlay.has_key((section, name)):
109 return self.overlay[(section, name)]
114 return self.overlay[(section, name)]
110 if self.cdata.has_option(section, name):
115 if self.cdata.has_option(section, name):
111 try:
116 try:
112 return self.cdata.getboolean(section, name)
117 return self.cdata.getboolean(section, name)
113 except ConfigParser.InterpolationError, inst:
118 except ConfigParser.InterpolationError, inst:
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
119 raise util.Abort(_("Error in configuration:\n%s") % inst)
115 if self.parentui is None:
120 if self.parentui is None:
116 return default
121 return default
117 else:
122 else:
118 return self.parentui.configbool(section, name, default)
123 return self.parentui.configbool(section, name, default)
119
124
120 def has_config(self, section):
125 def has_config(self, section):
121 '''tell whether section exists in config.'''
126 '''tell whether section exists in config.'''
122 return self.cdata.has_section(section)
127 return self.cdata.has_section(section)
123
128
124 def configitems(self, section):
129 def configitems(self, section):
125 items = {}
130 items = {}
126 if self.parentui is not None:
131 if self.parentui is not None:
127 items = dict(self.parentui.configitems(section))
132 items = dict(self.parentui.configitems(section))
128 if self.cdata.has_section(section):
133 if self.cdata.has_section(section):
129 try:
134 try:
130 items.update(dict(self.cdata.items(section)))
135 items.update(dict(self.cdata.items(section)))
131 except ConfigParser.InterpolationError, inst:
136 except ConfigParser.InterpolationError, inst:
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
137 raise util.Abort(_("Error in configuration:\n%s") % inst)
133 x = items.items()
138 x = items.items()
134 x.sort()
139 x.sort()
135 return x
140 return x
136
141
137 def walkconfig(self, seen=None):
142 def walkconfig(self, seen=None):
138 if seen is None:
143 if seen is None:
139 seen = {}
144 seen = {}
140 for (section, name), value in self.overlay.iteritems():
145 for (section, name), value in self.overlay.iteritems():
141 yield section, name, value
146 yield section, name, value
142 seen[section, name] = 1
147 seen[section, name] = 1
143 for section in self.cdata.sections():
148 for section in self.cdata.sections():
144 for name, value in self.cdata.items(section):
149 for name, value in self.cdata.items(section):
145 if (section, name) in seen: continue
150 if (section, name) in seen: continue
146 yield section, name, value.replace('\n', '\\n')
151 yield section, name, value.replace('\n', '\\n')
147 seen[section, name] = 1
152 seen[section, name] = 1
148 if self.parentui is not None:
153 if self.parentui is not None:
149 for parent in self.parentui.walkconfig(seen):
154 for parent in self.parentui.walkconfig(seen):
150 yield parent
155 yield parent
151
156
152 def extensions(self):
157 def extensions(self):
153 result = self.configitems("extensions")
158 result = self.configitems("extensions")
154 for i, (key, value) in enumerate(result):
159 for i, (key, value) in enumerate(result):
155 if value:
160 if value:
156 result[i] = (key, os.path.expanduser(value))
161 result[i] = (key, os.path.expanduser(value))
157 return result
162 return result
158
163
159 def hgignorefiles(self):
164 def hgignorefiles(self):
160 result = []
165 result = []
161 for key, value in self.configitems("ui"):
166 for key, value in self.configitems("ui"):
162 if key == 'ignore' or key.startswith('ignore.'):
167 if key == 'ignore' or key.startswith('ignore.'):
163 result.append(os.path.expanduser(value))
168 result.append(os.path.expanduser(value))
164 return result
169 return result
165
170
166 def configrevlog(self):
171 def configrevlog(self):
167 result = {}
172 result = {}
168 for key, value in self.configitems("revlog"):
173 for key, value in self.configitems("revlog"):
169 result[key.lower()] = value
174 result[key.lower()] = value
170 return result
175 return result
171
176
172 def username(self):
177 def username(self):
173 """Return default username to be used in commits.
178 """Return default username to be used in commits.
174
179
175 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
180 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
176 and stop searching if one of these is set.
181 and stop searching if one of these is set.
177 Abort if found username is an empty string to force specifying
182 Abort if found username is an empty string to force specifying
178 the commit user elsewhere, e.g. with line option or repo hgrc.
183 the commit user elsewhere, e.g. with line option or repo hgrc.
179 If not found, use ($LOGNAME or $USER or $LNAME or
184 If not found, use ($LOGNAME or $USER or $LNAME or
180 $USERNAME) +"@full.hostname".
185 $USERNAME) +"@full.hostname".
181 """
186 """
182 user = os.environ.get("HGUSER")
187 user = os.environ.get("HGUSER")
183 if user is None:
188 if user is None:
184 user = self.config("ui", "username")
189 user = self.config("ui", "username")
185 if user is None:
190 if user is None:
186 user = os.environ.get("EMAIL")
191 user = os.environ.get("EMAIL")
187 if user is None:
192 if user is None:
188 try:
193 try:
189 user = '%s@%s' % (util.getuser(), socket.getfqdn())
194 user = '%s@%s' % (util.getuser(), socket.getfqdn())
190 except KeyError:
195 except KeyError:
191 raise util.Abort(_("Please specify a username."))
196 raise util.Abort(_("Please specify a username."))
192 return user
197 return user
193
198
194 def shortuser(self, user):
199 def shortuser(self, user):
195 """Return a short representation of a user name or email address."""
200 """Return a short representation of a user name or email address."""
196 if not self.verbose: user = util.shortuser(user)
201 if not self.verbose: user = util.shortuser(user)
197 return user
202 return user
198
203
199 def expandpath(self, loc, default=None):
204 def expandpath(self, loc, default=None):
200 """Return repository location relative to cwd or from [paths]"""
205 """Return repository location relative to cwd or from [paths]"""
201 if "://" in loc or os.path.isdir(loc):
206 if "://" in loc or os.path.isdir(loc):
202 return loc
207 return loc
203
208
204 path = self.config("paths", loc)
209 path = self.config("paths", loc)
205 if not path and default is not None:
210 if not path and default is not None:
206 path = self.config("paths", default)
211 path = self.config("paths", default)
207 return path or loc
212 return path or loc
208
213
209 def write(self, *args):
214 def write(self, *args):
210 if self.header:
215 if self.header:
211 if self.header != self.prev_header:
216 if self.header != self.prev_header:
212 self.prev_header = self.header
217 self.prev_header = self.header
213 self.write(*self.header)
218 self.write(*self.header)
214 self.header = []
219 self.header = []
215 for a in args:
220 for a in args:
216 sys.stdout.write(str(a))
221 sys.stdout.write(str(a))
217
222
218 def write_header(self, *args):
223 def write_header(self, *args):
219 for a in args:
224 for a in args:
220 self.header.append(str(a))
225 self.header.append(str(a))
221
226
222 def write_err(self, *args):
227 def write_err(self, *args):
223 try:
228 try:
224 if not sys.stdout.closed: sys.stdout.flush()
229 if not sys.stdout.closed: sys.stdout.flush()
225 for a in args:
230 for a in args:
226 sys.stderr.write(str(a))
231 sys.stderr.write(str(a))
227 except IOError, inst:
232 except IOError, inst:
228 if inst.errno != errno.EPIPE:
233 if inst.errno != errno.EPIPE:
229 raise
234 raise
230
235
231 def flush(self):
236 def flush(self):
232 try: sys.stdout.flush()
237 try: sys.stdout.flush()
233 except: pass
238 except: pass
234 try: sys.stderr.flush()
239 try: sys.stderr.flush()
235 except: pass
240 except: pass
236
241
237 def readline(self):
242 def readline(self):
238 return sys.stdin.readline()[:-1]
243 return sys.stdin.readline()[:-1]
239 def prompt(self, msg, pat=None, default="y"):
244 def prompt(self, msg, pat=None, default="y"):
240 if not self.interactive: return default
245 if not self.interactive: return default
241 while 1:
246 while 1:
242 self.write(msg, " ")
247 self.write(msg, " ")
243 r = self.readline()
248 r = self.readline()
244 if not pat or re.match(pat, r):
249 if not pat or re.match(pat, r):
245 return r
250 return r
246 else:
251 else:
247 self.write(_("unrecognized response\n"))
252 self.write(_("unrecognized response\n"))
248 def getpass(self, prompt=None, default=None):
253 def getpass(self, prompt=None, default=None):
249 if not self.interactive: return default
254 if not self.interactive: return default
250 return getpass.getpass(prompt or _('password: '))
255 return getpass.getpass(prompt or _('password: '))
251 def status(self, *msg):
256 def status(self, *msg):
252 if not self.quiet: self.write(*msg)
257 if not self.quiet: self.write(*msg)
253 def warn(self, *msg):
258 def warn(self, *msg):
254 self.write_err(*msg)
259 self.write_err(*msg)
255 def note(self, *msg):
260 def note(self, *msg):
256 if self.verbose: self.write(*msg)
261 if self.verbose: self.write(*msg)
257 def debug(self, *msg):
262 def debug(self, *msg):
258 if self.debugflag: self.write(*msg)
263 if self.debugflag: self.write(*msg)
259 def edit(self, text, user):
264 def edit(self, text, user):
260 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
265 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
261 text=True)
266 text=True)
262 try:
267 try:
263 f = os.fdopen(fd, "w")
268 f = os.fdopen(fd, "w")
264 f.write(text)
269 f.write(text)
265 f.close()
270 f.close()
266
271
267 editor = (os.environ.get("HGEDITOR") or
272 editor = (os.environ.get("HGEDITOR") or
268 self.config("ui", "editor") or
273 self.config("ui", "editor") or
269 os.environ.get("EDITOR", "vi"))
274 os.environ.get("EDITOR", "vi"))
270
275
271 util.system("%s \"%s\"" % (editor, name),
276 util.system("%s \"%s\"" % (editor, name),
272 environ={'HGUSER': user},
277 environ={'HGUSER': user},
273 onerr=util.Abort, errprefix=_("edit failed"))
278 onerr=util.Abort, errprefix=_("edit failed"))
274
279
275 f = open(name)
280 f = open(name)
276 t = f.read()
281 t = f.read()
277 f.close()
282 f.close()
278 t = re.sub("(?m)^HG:.*\n", "", t)
283 t = re.sub("(?m)^HG:.*\n", "", t)
279 finally:
284 finally:
280 os.unlink(name)
285 os.unlink(name)
281
286
282 return t
287 return t
283
288
284 def print_exc(self):
289 def print_exc(self):
285 '''print exception traceback if traceback printing enabled.
290 '''print exception traceback if traceback printing enabled.
286 only to call in exception handler. returns true if traceback
291 only to call in exception handler. returns true if traceback
287 printed.'''
292 printed.'''
288 if self.traceback:
293 if self.traceback:
289 traceback.print_exc()
294 traceback.print_exc()
290 return self.traceback
295 return self.traceback
@@ -1,33 +1,33 b''
1 A simple testing framework
1 A simple testing framework
2
2
3 To run the tests, do:
3 To run the tests, do:
4
4
5 cd tests/
5 cd tests/
6 python run-tests.py
6 python run-tests.py
7
7
8 This finds all scripts in the test directory named test-* and executes
8 This finds all scripts in the test directory named test-* and executes
9 them. The scripts can be either shell scripts or Python. Each test is
9 them. The scripts can be either shell scripts or Python. Each test is
10 run in a temporary directory that is removed when the test is complete.
10 run in a temporary directory that is removed when the test is complete.
11
11
12 A test-<x> succeeds if the script returns success and its output
12 A test-<x> succeeds if the script returns success and its output
13 matches test-<x>.out. If the new output doesn't match, it is stored in
13 matches test-<x>.out. If the new output doesn't match, it is stored in
14 test-<x>.err.
14 test-<x>.err.
15
15
16 There are some tricky points here that you should be aware of when
16 There are some tricky points here that you should be aware of when
17 writing tests:
17 writing tests:
18
18
19 - hg commit and hg merge want user interaction
19 - hg commit and hg merge want user interaction
20
20
21 for commit use -m "text"
21 for commit use -m "text"
22 for hg merge, set HGMERGE to something noninteractive (like true or merge)
22 for hg merge, set HGMERGE to something noninteractive (like true or merge)
23
23
24 - changeset hashes will change based on user and date which make
24 - changeset hashes will change based on user and date which make
25 things like hg history output change
25 things like hg history output change
26
26
27 use commit -m "test" -u test -d "1000000 0"
27 use commit -m "test" -u test -d "1000000 0"
28
28
29 - diff will show the current time
29 - diff will show the current time
30
30
31 use hg diff | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" to strip
31 use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
32 dates
32 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
33
33 to strip dates
@@ -1,37 +1,37 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 set -e
3 set -e
4
4
5 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
5 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
6 echo "[extensions]" >> $HGTMP/.hgrc
6 echo "[extensions]" >> $HGTMP/.hgrc
7 echo "hbisect=" >> $HGTMP/.hgrc
7 echo "hbisect=" >> $HGTMP/.hgrc
8
8
9 echo % init
9 echo % init
10 hg init
10 hg init
11
11
12 echo % committing changes
12 echo % committing changes
13 count=0
13 count=0
14 echo > a
14 echo > a
15 while test $count -lt 32 ; do
15 while test $count -lt 32 ; do
16 echo 'a' >> a
16 echo 'a' >> a
17 test $count -eq 0 && hg add
17 test $count -eq 0 && hg add
18 hg ci -m "msg $count" -d "$count 0"
18 hg ci -m "msg $count" -d "$count 0"
19 echo % committed changeset $count
19 echo % committed changeset $count
20 count=$(( $count + 1 ))
20 count=`expr $count + 1`
21 done
21 done
22
22
23 echo % log
23 echo % log
24 hg log
24 hg log
25
25
26 echo % hg up -C
26 echo % hg up -C
27 hg up -C
27 hg up -C
28
28
29 echo % bisect test
29 echo % bisect test
30 hg bisect init
30 hg bisect init
31 hg bisect bad
31 hg bisect bad
32 hg bisect good 1
32 hg bisect good 1
33 hg bisect good
33 hg bisect good
34 hg bisect good
34 hg bisect good
35 hg bisect good
35 hg bisect good
36 hg bisect bad
36 hg bisect bad
37 hg bisect good
37 hg bisect good
@@ -1,26 +1,30 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "extdiff=" >> $HGTMP/.hgrc
5 echo "extdiff=" >> $HGTMP/.hgrc
6
6
7 hg init a
7 hg init a
8 cd a
8 cd a
9 echo a > a
9 echo a > a
10 hg add
10 hg add
11 hg extdiff -o -Nr
11 diff -N /dev/null /dev/null 2> /dev/null
12 if [ $? -ne 0 ]; then
13 opt="-p gdiff"
14 fi
15 hg extdiff -o -Nr $opt
12
16
13 echo "[extdiff]" >> $HGTMP/.hgrc
17 echo "[extdiff]" >> $HGTMP/.hgrc
14 echo "cmd.falabala=echo" >> $HGTMP/.hgrc
18 echo "cmd.falabala=echo" >> $HGTMP/.hgrc
15 echo "opts.falabala=diffing" >> $HGTMP/.hgrc
19 echo "opts.falabala=diffing" >> $HGTMP/.hgrc
16
20
17 hg falabala
21 hg falabala
18
22
19 hg help falabala
23 hg help falabala
20
24
21 hg ci -d '0 0' -mtest1
25 hg ci -d '0 0' -mtest1
22
26
23 echo b >> a
27 echo b >> a
24 hg ci -d '1 0' -mtest2
28 hg ci -d '1 0' -mtest2
25
29
26 hg falabala -r 0:1 || echo "diff-like tools yield a non-zero exit code"
30 hg falabala -r 0:1 || echo "diff-like tools yield a non-zero exit code"
@@ -1,46 +1,52 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 cd a
4 cd a
5
5
6 echo start > start
6 echo start > start
7 hg ci -Amstart -d '0 0'
7 hg ci -Amstart -d '0 0'
8 echo new > new
8 echo new > new
9 hg ci -Amnew -d '0 0'
9 hg ci -Amnew -d '0 0'
10 echo '% new file'
10 echo '% new file'
11 hg diff --git -r 0 | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
11 hg diff --git -r 0 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
12 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
12
13
13 hg cp new copy
14 hg cp new copy
14 hg ci -mcopy -d '0 0'
15 hg ci -mcopy -d '0 0'
15 echo '% copy'
16 echo '% copy'
16 hg diff --git -r 1:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
17 hg diff --git -r 1:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
18 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
17
19
18 hg mv copy rename
20 hg mv copy rename
19 hg ci -mrename -d '0 0'
21 hg ci -mrename -d '0 0'
20 echo '% rename'
22 echo '% rename'
21 hg diff --git -r 2:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
23 hg diff --git -r 2:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
24 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
22
25
23 hg rm rename
26 hg rm rename
24 hg ci -mdelete -d '0 0'
27 hg ci -mdelete -d '0 0'
25 echo '% delete'
28 echo '% delete'
26 hg diff --git -r 3:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
29 hg diff --git -r 3:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
30 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
27
31
28 cat > src <<EOF
32 cat > src <<EOF
29 1
33 1
30 2
34 2
31 3
35 3
32 4
36 4
33 5
37 5
34 EOF
38 EOF
35 hg ci -Amsrc -d '0 0'
39 hg ci -Amsrc -d '0 0'
36 chmod +x src
40 chmod +x src
37 hg ci -munexec -d '0 0'
41 hg ci -munexec -d '0 0'
38 echo '% chmod 644'
42 echo '% chmod 644'
39 hg diff --git -r 5:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
43 hg diff --git -r 5:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
44 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
40
45
41 hg mv src dst
46 hg mv src dst
42 chmod -x dst
47 chmod -x dst
43 echo a >> dst
48 echo a >> dst
44 hg ci -mrenamemod -d '0 0'
49 hg ci -mrenamemod -d '0 0'
45 echo '% rename+mod+chmod'
50 echo '% rename+mod+chmod'
46 hg diff --git -r 6:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
51 hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
52 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
@@ -1,128 +1,155 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
6
7 echo % help
7 echo % help
8 hg help mq
8 hg help mq
9
9
10 hg init a
10 hg init a
11 cd a
11 cd a
12 echo a > a
12 echo a > a
13 hg ci -Ama
13 hg ci -Ama
14
14
15 hg clone . ../k
15 hg clone . ../k
16
16
17 mkdir b
17 mkdir b
18 echo z > b/z
18 echo z > b/z
19 hg ci -Ama
19 hg ci -Ama
20
20
21 echo % qinit
21 echo % qinit
22
22
23 hg qinit
23 hg qinit
24
24
25 cd ..
25 cd ..
26 hg init b
26 hg init b
27
27
28 echo % -R qinit
28 echo % -R qinit
29
29
30 hg -R b qinit
30 hg -R b qinit
31
31
32 hg init c
32 hg init c
33
33
34 echo % qinit -c
34 echo % qinit -c
35
35
36 hg --cwd c qinit -c
36 hg --cwd c qinit -c
37 hg -R c/.hg/patches st
37 hg -R c/.hg/patches st
38
38
39 echo % qnew implies add
39 echo % qnew implies add
40
40
41 hg -R c qnew test.patch
41 hg -R c qnew test.patch
42 hg -R c/.hg/patches st
42 hg -R c/.hg/patches st
43
43
44 cd a
44 cd a
45
45
46 echo % qnew -m
46 echo % qnew -m
47
47
48 hg qnew -m 'foo bar' test.patch
48 hg qnew -m 'foo bar' test.patch
49 cat .hg/patches/test.patch
49 cat .hg/patches/test.patch
50
50
51 echo % qrefresh
51 echo % qrefresh
52
52
53 echo a >> a
53 echo a >> a
54 hg qrefresh
54 hg qrefresh
55 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
55 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
56 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
56 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
57 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
57 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
58
58
59 echo % qpop
59 echo % qpop
60
60
61 hg qpop
61 hg qpop
62
62
63 echo % qpush
63 echo % qpush
64
64
65 hg qpush
65 hg qpush
66
66
67 cd ..
67 cd ..
68
68
69 echo % pop/push outside repo
69 echo % pop/push outside repo
70
70
71 hg -R a qpop
71 hg -R a qpop
72 hg -R a qpush
72 hg -R a qpush
73
73
74 cd a
74 cd a
75 hg qnew test2.patch
75 hg qnew test2.patch
76
76
77 echo % qrefresh in subdir
77 echo % qrefresh in subdir
78
78
79 cd b
79 cd b
80 echo a > a
80 echo a > a
81 hg add a
81 hg add a
82 hg qrefresh
82 hg qrefresh
83
83
84 echo % pop/push -a in subdir
84 echo % pop/push -a in subdir
85
85
86 hg qpop -a
86 hg qpop -a
87 hg --traceback qpush -a
87 hg --traceback qpush -a
88
88
89 echo % qseries
89 echo % qseries
90 hg qseries
90 hg qseries
91
91
92 echo % qapplied
92 echo % qapplied
93 hg qapplied
93 hg qapplied
94
94
95 echo % qtop
95 echo % qtop
96 hg qtop
96 hg qtop
97
97
98 echo % qprev
98 echo % qprev
99 hg qprev
99 hg qprev
100
100
101 echo % qnext
101 echo % qnext
102 hg qnext
102 hg qnext
103
103
104 echo % pop, qnext, qprev, qapplied
104 echo % pop, qnext, qprev, qapplied
105 hg qpop
105 hg qpop
106 hg qnext
106 hg qnext
107 hg qprev
107 hg qprev
108 hg qapplied
108 hg qapplied
109
109
110 echo % commit should fail
110 echo % commit should fail
111 hg commit
111 hg commit
112
112
113 echo % push should fail
113 echo % push should fail
114 hg push ../../k
114 hg push ../../k
115
115
116 echo % qunapplied
116 echo % qunapplied
117 hg qunapplied
117 hg qunapplied
118
118
119 echo % push should succeed
119 echo % push should succeed
120 hg qpop -a
120 hg qpop -a
121 hg push ../../k
121 hg push ../../k
122
122
123 echo % strip
123 echo % strip
124 cd ../../b
124 cd ../../b
125 echo x>x
125 echo x>x
126 hg ci -Ama
126 hg ci -Ama
127 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
127 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
128 hg unbundle .hg/strip-backup/*
128 hg unbundle .hg/strip-backup/*
129
130 cat >>$HGTMP/.hgrc <<EOF
131 [diff]
132 git = True
133 EOF
134 cd ..
135 hg init git
136 cd git
137 hg qinit
138
139 hg qnew -m'new file' new
140 echo foo > new
141 chmod +x new
142 hg add new
143 hg qrefresh
144 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
145 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
146
147 hg qnew -m'copy file' copy
148 hg cp new copy
149 hg qrefresh
150 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
151 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
152
153 hg qpop
154 hg qpush
155 hg qdiff
@@ -1,129 +1,148 b''
1 % help
1 % help
2 mq extension - patch management and development
2 mq extension - patch management and development
3
3
4 This extension lets you work with a stack of patches in a Mercurial
4 This extension lets you work with a stack of patches in a Mercurial
5 repository. It manages two stacks of patches - all known patches, and
5 repository. It manages two stacks of patches - all known patches, and
6 applied patches (subset of known patches).
6 applied patches (subset of known patches).
7
7
8 Known patches are represented as patch files in the .hg/patches
8 Known patches are represented as patch files in the .hg/patches
9 directory. Applied patches are both patch files and changesets.
9 directory. Applied patches are both patch files and changesets.
10
10
11 Common tasks (use "hg help command" for more details):
11 Common tasks (use "hg help command" for more details):
12
12
13 prepare repository to work with patches qinit
13 prepare repository to work with patches qinit
14 create new patch qnew
14 create new patch qnew
15 import existing patch qimport
15 import existing patch qimport
16
16
17 print patch series qseries
17 print patch series qseries
18 print applied patches qapplied
18 print applied patches qapplied
19 print name of top applied patch qtop
19 print name of top applied patch qtop
20
20
21 add known patch to applied stack qpush
21 add known patch to applied stack qpush
22 remove patch from applied stack qpop
22 remove patch from applied stack qpop
23 refresh contents of top applied patch qrefresh
23 refresh contents of top applied patch qrefresh
24
24
25 list of commands (use "hg help -v mq" to show aliases and global options):
25 list of commands (use "hg help -v mq" to show aliases and global options):
26
26
27 qapplied print the patches already applied
27 qapplied print the patches already applied
28 qclone clone main and patch repository at same time
28 qclone clone main and patch repository at same time
29 qcommit commit changes in the queue repository
29 qcommit commit changes in the queue repository
30 qdelete remove patches from queue
30 qdelete remove patches from queue
31 qdiff diff of the current patch
31 qdiff diff of the current patch
32 qfold fold the named patches into the current patch
32 qfold fold the named patches into the current patch
33 qguard set or print guards for a patch
33 qguard set or print guards for a patch
34 qheader Print the header of the topmost or specified patch
34 qheader Print the header of the topmost or specified patch
35 qimport import a patch
35 qimport import a patch
36 qinit init a new queue repository
36 qinit init a new queue repository
37 qnew create a new patch
37 qnew create a new patch
38 qnext print the name of the next patch
38 qnext print the name of the next patch
39 qpop pop the current patch off the stack
39 qpop pop the current patch off the stack
40 qprev print the name of the previous patch
40 qprev print the name of the previous patch
41 qpush push the next patch onto the stack
41 qpush push the next patch onto the stack
42 qrefresh update the current patch
42 qrefresh update the current patch
43 qrename rename a patch
43 qrename rename a patch
44 qrestore restore the queue state saved by a rev
44 qrestore restore the queue state saved by a rev
45 qsave save current queue state
45 qsave save current queue state
46 qselect set or print guarded patches to push
46 qselect set or print guarded patches to push
47 qseries print the entire series file
47 qseries print the entire series file
48 qtop print the name of the current patch
48 qtop print the name of the current patch
49 qunapplied print the patches not yet applied
49 qunapplied print the patches not yet applied
50 strip strip a revision and all later revs on the same branch
50 strip strip a revision and all later revs on the same branch
51 adding a
51 adding a
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 adding b/z
53 adding b/z
54 % qinit
54 % qinit
55 % -R qinit
55 % -R qinit
56 % qinit -c
56 % qinit -c
57 A .hgignore
57 A .hgignore
58 A series
58 A series
59 % qnew implies add
59 % qnew implies add
60 A .hgignore
60 A .hgignore
61 A series
61 A series
62 A test.patch
62 A test.patch
63 % qnew -m
63 % qnew -m
64 foo bar
64 foo bar
65 % qrefresh
65 % qrefresh
66 foo bar
66 foo bar
67
67
68 diff -r xa
68 diff -r xa
69 --- a/a
69 --- a/a
70 +++ b/a
70 +++ b/a
71 @@ -1,1 +1,2 @@ a
71 @@ -1,1 +1,2 @@ a
72 a
72 a
73 +a
73 +a
74 % qpop
74 % qpop
75 Patch queue now empty
75 Patch queue now empty
76 % qpush
76 % qpush
77 applying test.patch
77 applying test.patch
78 Now at: test.patch
78 Now at: test.patch
79 % pop/push outside repo
79 % pop/push outside repo
80 Patch queue now empty
80 Patch queue now empty
81 applying test.patch
81 applying test.patch
82 Now at: test.patch
82 Now at: test.patch
83 % qrefresh in subdir
83 % qrefresh in subdir
84 % pop/push -a in subdir
84 % pop/push -a in subdir
85 Patch queue now empty
85 Patch queue now empty
86 applying test.patch
86 applying test.patch
87 applying test2.patch
87 applying test2.patch
88 Now at: test2.patch
88 Now at: test2.patch
89 % qseries
89 % qseries
90 test.patch
90 test.patch
91 test2.patch
91 test2.patch
92 % qapplied
92 % qapplied
93 test.patch
93 test.patch
94 test2.patch
94 test2.patch
95 % qtop
95 % qtop
96 test2.patch
96 test2.patch
97 % qprev
97 % qprev
98 test.patch
98 test.patch
99 % qnext
99 % qnext
100 All patches applied
100 All patches applied
101 % pop, qnext, qprev, qapplied
101 % pop, qnext, qprev, qapplied
102 Now at: test.patch
102 Now at: test.patch
103 test2.patch
103 test2.patch
104 Only one patch applied
104 Only one patch applied
105 test.patch
105 test.patch
106 % commit should fail
106 % commit should fail
107 abort: cannot commit over an applied mq patch
107 abort: cannot commit over an applied mq patch
108 % push should fail
108 % push should fail
109 pushing to ../../k
109 pushing to ../../k
110 abort: source has mq patches applied
110 abort: source has mq patches applied
111 % qunapplied
111 % qunapplied
112 test2.patch
112 test2.patch
113 % push should succeed
113 % push should succeed
114 Patch queue now empty
114 Patch queue now empty
115 pushing to ../../k
115 pushing to ../../k
116 searching for changes
116 searching for changes
117 adding changesets
117 adding changesets
118 adding manifests
118 adding manifests
119 adding file changes
119 adding file changes
120 added 1 changesets with 1 changes to 1 files
120 added 1 changesets with 1 changes to 1 files
121 % strip
121 % strip
122 adding x
122 adding x
123 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 saving bundle to
124 saving bundle to
125 adding changesets
125 adding changesets
126 adding manifests
126 adding manifests
127 adding file changes
127 adding file changes
128 added 1 changesets with 1 changes to 1 files
128 added 1 changesets with 1 changes to 1 files
129 (run 'hg update' to get a working copy)
129 (run 'hg update' to get a working copy)
130 new file
131
132 diff --git a/new b/new
133 new file mode 100755
134 --- /dev/null
135 +++ b/new
136 @@ -0,0 +1,1 @@
137 +foo
138 copy file
139
140 diff --git a/new b/copy
141 copy from new
142 copy to copy
143 Now at: new
144 applying copy
145 Now at: copy
146 diff --git a/new b/copy
147 copy from new
148 copy to copy
General Comments 0
You need to be logged in to leave comments. Login now