##// END OF EJS Templates
merge.
Vadim Gelfer -
r2956:6dddcba7 merge default
parent child Browse files
Show More
@@ -0,0 +1,99 b''
1 # fetch.py - pull and merge remote changes
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from mercurial.demandload import *
9 from mercurial.i18n import gettext as _
10 from mercurial.node import *
11 demandload(globals(), 'mercurial:commands,hg,node,util')
12
13 def fetch(ui, repo, source='default', **opts):
14 '''Pull changes from a remote repository, merge new changes if needed.
15
16 This finds all changes from the repository at the specified path
17 or URL and adds them to the local repository.
18
19 If the pulled changes add a new head, the head is automatically
20 merged, and the result of the merge is committed. Otherwise, the
21 working directory is updated.'''
22
23 def postincoming(other, modheads):
24 if modheads == 0:
25 return 0
26 if modheads == 1:
27 return hg.clean(repo, repo.changelog.tip(), wlock=wlock)
28 newheads = repo.heads(parent)
29 newchildren = [n for n in repo.heads(parent) if n != parent]
30 newparent = parent
31 if newchildren:
32 newparent = newchildren[0]
33 hg.clean(repo, newparent, wlock=wlock)
34 newheads = [n for n in repo.heads() if n != newparent]
35 err = False
36 if newheads:
37 ui.status(_('merging with new head %d:%s\n') %
38 (repo.changelog.rev(newheads[0]), short(newheads[0])))
39 err = hg.merge(repo, newheads[0], remind=False, wlock=wlock)
40 if not err and len(newheads) > 1:
41 ui.status(_('not merging with %d other new heads '
42 '(use "hg heads" and "hg merge" to merge them)') %
43 (len(newheads) - 1))
44 if not err:
45 mod, add, rem = repo.status(wlock=wlock)[:3]
46 message = (commands.logmessage(opts) or
47 (_('Automated merge with %s') % other.url()))
48 n = repo.commit(mod + add + rem, message,
49 opts['user'], opts['date'], lock=lock, wlock=wlock,
50 force_editor=opts.get('force_editor'))
51 ui.status(_('new changeset %d:%s merges remote changes '
52 'with local\n') % (repo.changelog.rev(n),
53 short(n)))
54 def pull():
55 commands.setremoteconfig(ui, opts)
56
57 other = hg.repository(ui, ui.expandpath(source))
58 ui.status(_('pulling from %s\n') % ui.expandpath(source))
59 revs = None
60 if opts['rev'] and not other.local():
61 raise util.Abort(_("fetch -r doesn't work for remote repositories yet"))
62 elif opts['rev']:
63 revs = [other.lookup(rev) for rev in opts['rev']]
64 modheads = repo.pull(other, heads=revs, lock=lock)
65 return postincoming(other, modheads)
66
67 parent, p2 = repo.dirstate.parents()
68 if parent != repo.changelog.tip():
69 raise util.Abort(_('working dir not at tip '
70 '(use "hg update" to check out tip)'))
71 if p2 != nullid:
72 raise util.Abort(_('outstanding uncommitted merge'))
73 wlock = repo.wlock()
74 lock = repo.lock()
75 try:
76 mod, add, rem = repo.status(wlock=wlock)[:3]
77 if mod or add or rem:
78 raise util.Abort(_('outstanding uncommitted changes'))
79 if len(repo.heads()) > 1:
80 raise util.Abort(_('multiple heads in this repository '
81 '(use "hg heads" and "hg merge" to merge)'))
82 return pull()
83 finally:
84 lock.release()
85 wlock.release()
86
87 cmdtable = {
88 'fetch':
89 (fetch,
90 [('e', 'ssh', '', _('specify ssh command to use')),
91 ('m', 'message', '', _('use <text> as commit message')),
92 ('l', 'logfile', '', _('read the commit message from <file>')),
93 ('d', 'date', '', _('record datecode as commit date')),
94 ('u', 'user', '', _('record user as commiter')),
95 ('r', 'rev', [], _('a specific revision you would like to pull')),
96 ('f', 'force-editor', None, _('edit commit message')),
97 ('', 'remotecmd', '', _('hg command to run on the remote side'))],
98 'hg fetch [SOURCE]'),
99 }
@@ -0,0 +1,111 b''
1 # commands.py - command processing for mercurial
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from demandload import demandload
9 from node import *
10 from i18n import gettext as _
11 demandload(globals(), 'util')
12 demandload(globals(), 'os sys')
13
14 def make_filename(repo, pat, node,
15 total=None, seqno=None, revwidth=None, pathname=None):
16 node_expander = {
17 'H': lambda: hex(node),
18 'R': lambda: str(repo.changelog.rev(node)),
19 'h': lambda: short(node),
20 }
21 expander = {
22 '%': lambda: '%',
23 'b': lambda: os.path.basename(repo.root),
24 }
25
26 try:
27 if node:
28 expander.update(node_expander)
29 if node and revwidth is not None:
30 expander['r'] = (lambda:
31 str(repo.changelog.rev(node)).zfill(revwidth))
32 if total is not None:
33 expander['N'] = lambda: str(total)
34 if seqno is not None:
35 expander['n'] = lambda: str(seqno)
36 if total is not None and seqno is not None:
37 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
38 if pathname is not None:
39 expander['s'] = lambda: os.path.basename(pathname)
40 expander['d'] = lambda: os.path.dirname(pathname) or '.'
41 expander['p'] = lambda: pathname
42
43 newname = []
44 patlen = len(pat)
45 i = 0
46 while i < patlen:
47 c = pat[i]
48 if c == '%':
49 i += 1
50 c = pat[i]
51 c = expander[c]()
52 newname.append(c)
53 i += 1
54 return ''.join(newname)
55 except KeyError, inst:
56 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
57 inst.args[0])
58
59 def make_file(repo, pat, node=None,
60 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
61 if not pat or pat == '-':
62 return 'w' in mode and sys.stdout or sys.stdin
63 if hasattr(pat, 'write') and 'w' in mode:
64 return pat
65 if hasattr(pat, 'read') and 'r' in mode:
66 return pat
67 return open(make_filename(repo, pat, node, total, seqno, revwidth,
68 pathname),
69 mode)
70
71 def matchpats(repo, pats=[], opts={}, head=''):
72 cwd = repo.getcwd()
73 if not pats and cwd:
74 opts['include'] = [os.path.join(cwd, i)
75 for i in opts.get('include', [])]
76 opts['exclude'] = [os.path.join(cwd, x)
77 for x in opts.get('exclude', [])]
78 cwd = ''
79 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
80 opts.get('exclude'), head)
81
82 def makewalk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
83 files, matchfn, anypats = matchpats(repo, pats, opts, head)
84 exact = dict(zip(files, files))
85 def walk():
86 for src, fn in repo.walk(node=node, files=files, match=matchfn,
87 badmatch=badmatch):
88 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
89 return files, matchfn, walk()
90
91 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
92 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
93 for r in results:
94 yield r
95
96 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None):
97 if dry_run is None:
98 dry_run = opts.get('dry_run')
99 add, remove = [], []
100 for src, abs, rel, exact in walk(repo, pats, opts):
101 if src == 'f' and repo.dirstate.state(abs) == '?':
102 add.append(abs)
103 if repo.ui.verbose or not exact:
104 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
105 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
106 remove.append(abs)
107 if repo.ui.verbose or not exact:
108 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
109 if not dry_run:
110 repo.add(add, wlock=wlock)
111 repo.remove(remove, wlock=wlock)
@@ -0,0 +1,68 b''
1 # mail.py - mail sending bits for mercurial
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from i18n import gettext as _
9 from demandload import *
10 demandload(globals(), "os re smtplib templater util")
11
12 def _smtp(ui):
13 '''send mail using smtp.'''
14
15 local_hostname = ui.config('smtp', 'local_hostname')
16 s = smtplib.SMTP(local_hostname=local_hostname)
17 mailhost = ui.config('smtp', 'host')
18 if not mailhost:
19 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
20 mailport = int(ui.config('smtp', 'port', 25))
21 self.note(_('sending mail: smtp host %s, port %s\n') %
22 (mailhost, mailport))
23 s.connect(host=mailhost, port=mailport)
24 if ui.configbool('smtp', 'tls'):
25 ui.note(_('(using tls)\n'))
26 s.ehlo()
27 s.starttls()
28 s.ehlo()
29 username = ui.config('smtp', 'username')
30 password = ui.config('smtp', 'password')
31 if username and password:
32 ui.note(_('(authenticating to mail server as %s)\n') %
33 (username))
34 s.login(username, password)
35 return s
36
37 class _sendmail(object):
38 '''send mail using sendmail.'''
39
40 def __init__(self, ui, program):
41 self.ui = ui
42 self.program = program
43
44 def sendmail(self, sender, recipients, msg):
45 cmdline = '%s -f %s %s' % (
46 self.program, templater.email(sender),
47 ' '.join(map(templater.email, recipients)))
48 self.ui.note(_('sending mail: %s\n') % cmdline)
49 fp = os.popen(cmdline, 'w')
50 fp.write(msg)
51 ret = fp.close()
52 if ret:
53 raise util.Abort('%s %s' % (
54 os.path.basename(self.program.split(None, 1)[0]),
55 util.explain_exit(ret)[0]))
56
57 def connect(ui):
58 '''make a mail connection. object returned has one method, sendmail.
59 call as sendmail(sender, list-of-recipients, msg).'''
60
61 method = ui.config('email', 'method', 'smtp')
62 if method == 'smtp':
63 return _smtp(ui)
64
65 return _sendmail(ui, method)
66
67 def sendmail(ui, sender, recipients, msg):
68 return connect(ui).sendmail(sender, recipients, msg)
@@ -0,0 +1,339 b''
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from node import *
9 from i18n import gettext as _
10 from demandload import *
11 demandload(globals(), "util os tempfile")
12
13 def fmerge(f, local, other, ancestor):
14 """merge executable flags"""
15 a, b, c = ancestor.execf(f), local.execf(f), other.execf(f)
16 return ((a^b) | (a^c)) ^ a
17
18 def merge3(repo, fn, my, other, p1, p2):
19 """perform a 3-way merge in the working directory"""
20
21 def temp(prefix, node):
22 pre = "%s~%s." % (os.path.basename(fn), prefix)
23 (fd, name) = tempfile.mkstemp(prefix=pre)
24 f = os.fdopen(fd, "wb")
25 repo.wwrite(fn, fl.read(node), f)
26 f.close()
27 return name
28
29 fl = repo.file(fn)
30 base = fl.ancestor(my, other)
31 a = repo.wjoin(fn)
32 b = temp("base", base)
33 c = temp("other", other)
34
35 repo.ui.note(_("resolving %s\n") % fn)
36 repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
37 (fn, short(my), short(other), short(base)))
38
39 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
40 or "hgmerge")
41 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
42 environ={'HG_FILE': fn,
43 'HG_MY_NODE': p1,
44 'HG_OTHER_NODE': p2,
45 'HG_FILE_MY_NODE': hex(my),
46 'HG_FILE_OTHER_NODE': hex(other),
47 'HG_FILE_BASE_NODE': hex(base)})
48 if r:
49 repo.ui.warn(_("merging %s failed!\n") % fn)
50
51 os.unlink(b)
52 os.unlink(c)
53 return r
54
55 def update(repo, node, branchmerge=False, force=False, partial=None,
56 wlock=None, show_stats=True, remind=True):
57
58 overwrite = force and not branchmerge
59 forcemerge = force and branchmerge
60
61 if not wlock:
62 wlock = repo.wlock()
63
64 ### check phase
65
66 pl = repo.dirstate.parents()
67 if not overwrite and pl[1] != nullid:
68 raise util.Abort(_("outstanding uncommitted merges"))
69
70 p1, p2 = pl[0], node
71 pa = repo.changelog.ancestor(p1, p2)
72
73 # is there a linear path from p1 to p2?
74 linear_path = (pa == p1 or pa == p2)
75 if branchmerge and linear_path:
76 raise util.Abort(_("there is nothing to merge, just use "
77 "'hg update' or look at 'hg heads'"))
78
79 if not overwrite and not linear_path and not branchmerge:
80 raise util.Abort(_("update spans branches, use 'hg merge' "
81 "or 'hg update -C' to lose changes"))
82
83 modified, added, removed, deleted, unknown = repo.status()[:5]
84 if branchmerge and not forcemerge:
85 if modified or added or removed:
86 raise util.Abort(_("outstanding uncommitted changes"))
87
88 m1n = repo.changelog.read(p1)[0]
89 m2n = repo.changelog.read(p2)[0]
90 man = repo.manifest.ancestor(m1n, m2n)
91 m1 = repo.manifest.read(m1n)
92 m2 = repo.manifest.read(m2n).copy()
93 ma = repo.manifest.read(man)
94
95 if not force:
96 for f in unknown:
97 if f in m2:
98 if repo.file(f).cmp(m2[f], repo.wread(f)):
99 raise util.Abort(_("'%s' already exists in the working"
100 " dir and differs from remote") % f)
101
102 # resolve the manifest to determine which files
103 # we care about merging
104 repo.ui.note(_("resolving manifests\n"))
105 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
106 (overwrite, branchmerge, bool(partial), linear_path))
107 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
108 (short(man), short(m1n), short(m2n)))
109
110 merge = {}
111 get = {}
112 remove = []
113 forget = []
114
115 # construct a working dir manifest
116 mw = m1.copy()
117 umap = dict.fromkeys(unknown)
118
119 for f in added + modified + unknown:
120 mw[f] = ""
121 # is the wfile new and matches m2?
122 if (f not in m1 and f in m2 and
123 not repo.file(f).cmp(m2[f], repo.wread(f))):
124 mw[f] = m2[f]
125
126 mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
127
128 for f in deleted + removed:
129 if f in mw:
130 del mw[f]
131
132 # If we're jumping between revisions (as opposed to merging),
133 # and if neither the working directory nor the target rev has
134 # the file, then we need to remove it from the dirstate, to
135 # prevent the dirstate from listing the file when it is no
136 # longer in the manifest.
137 if linear_path and f not in m2:
138 forget.append(f)
139
140 # Compare manifests
141 for f, n in mw.iteritems():
142 if partial and not partial(f):
143 continue
144 if f in m2:
145 s = 0
146
147 # are files different?
148 if n != m2[f]:
149 a = ma.get(f, nullid)
150 # are both different from the ancestor?
151 if n != a and m2[f] != a:
152 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
153 merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f])
154 s = 1
155 # are we clobbering?
156 # is remote's version newer?
157 # or are we going back in time?
158 elif overwrite or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
159 repo.ui.debug(_(" remote %s is newer, get\n") % f)
160 get[f] = (m2.execf(f), m2[f])
161 s = 1
162 elif f in umap or f in added:
163 # this unknown file is the same as the checkout
164 # we need to reset the dirstate if the file was added
165 get[f] = (m2.execf(f), m2[f])
166
167 if not s and mw.execf(f) != m2.execf(f):
168 if overwrite:
169 repo.ui.debug(_(" updating permissions for %s\n") % f)
170 util.set_exec(repo.wjoin(f), m2.execf(f))
171 else:
172 if fmerge(f, mw, m2, ma) != mw.execf(f):
173 repo.ui.debug(_(" updating permissions for %s\n")
174 % f)
175 util.set_exec(repo.wjoin(f), mode)
176 del m2[f]
177 elif f in ma:
178 if n != ma[f]:
179 r = _("d")
180 if not overwrite and (linear_path or branchmerge):
181 r = repo.ui.prompt(
182 (_(" local changed %s which remote deleted\n") % f) +
183 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
184 if r == _("d"):
185 remove.append(f)
186 else:
187 repo.ui.debug(_("other deleted %s\n") % f)
188 remove.append(f) # other deleted it
189 else:
190 # file is created on branch or in working directory
191 if overwrite and f not in umap:
192 repo.ui.debug(_("remote deleted %s, clobbering\n") % f)
193 remove.append(f)
194 elif n == m1.get(f, nullid): # same as parent
195 if p2 == pa: # going backwards?
196 repo.ui.debug(_("remote deleted %s\n") % f)
197 remove.append(f)
198 else:
199 repo.ui.debug(_("local modified %s, keeping\n") % f)
200 else:
201 repo.ui.debug(_("working dir created %s, keeping\n") % f)
202
203 for f, n in m2.iteritems():
204 if partial and not partial(f):
205 continue
206 if f[0] == "/":
207 continue
208 if f in ma and n != ma[f]:
209 r = _("k")
210 if not overwrite and (linear_path or branchmerge):
211 r = repo.ui.prompt(
212 (_("remote changed %s which local deleted\n") % f) +
213 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
214 if r == _("k"):
215 get[f] = (m2.execf(f), n)
216 elif f not in ma:
217 repo.ui.debug(_("remote created %s\n") % f)
218 get[f] = (m2.execf(f), n)
219 else:
220 if overwrite or p2 == pa: # going backwards?
221 repo.ui.debug(_("local deleted %s, recreating\n") % f)
222 get[f] = (m2.execf(f), n)
223 else:
224 repo.ui.debug(_("local deleted %s\n") % f)
225
226 del mw, m1, m2, ma
227
228 ### apply phase
229
230 if overwrite:
231 for f in merge:
232 get[f] = merge[f][:2]
233 merge = {}
234
235 if linear_path or overwrite:
236 # we don't need to do any magic, just jump to the new rev
237 p1, p2 = p2, nullid
238
239 xp1 = hex(p1)
240 xp2 = hex(p2)
241 if p2 == nullid: xxp2 = ''
242 else: xxp2 = xp2
243
244 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
245
246 # get the files we don't need to change
247 files = get.keys()
248 files.sort()
249 for f in files:
250 flag, node = get[f]
251 if f[0] == "/":
252 continue
253 repo.ui.note(_("getting %s\n") % f)
254 t = repo.file(f).read(node)
255 repo.wwrite(f, t)
256 util.set_exec(repo.wjoin(f), flag)
257
258 # merge the tricky bits
259 unresolved = []
260 files = merge.keys()
261 files.sort()
262 for f in files:
263 repo.ui.status(_("merging %s\n") % f)
264 flag, my, other = merge[f]
265 ret = merge3(repo, f, my, other, xp1, xp2)
266 if ret:
267 unresolved.append(f)
268 util.set_exec(repo.wjoin(f), flag)
269
270 remove.sort()
271 for f in remove:
272 repo.ui.note(_("removing %s\n") % f)
273 util.audit_path(f)
274 try:
275 util.unlink(repo.wjoin(f))
276 except OSError, inst:
277 if inst.errno != errno.ENOENT:
278 repo.ui.warn(_("update failed to remove %s: %s!\n") %
279 (f, inst.strerror))
280
281 # update dirstate
282 if not partial:
283 repo.dirstate.setparents(p1, p2)
284 repo.dirstate.forget(forget)
285 if branchmerge:
286 repo.dirstate.update(remove, 'r')
287 else:
288 repo.dirstate.forget(remove)
289
290 files = get.keys()
291 files.sort()
292 for f in files:
293 if branchmerge:
294 repo.dirstate.update([f], 'n', st_mtime=-1)
295 else:
296 repo.dirstate.update([f], 'n')
297
298 files = merge.keys()
299 files.sort()
300 for f in files:
301 if branchmerge:
302 # We've done a branch merge, mark this file as merged
303 # so that we properly record the merger later
304 repo.dirstate.update([f], 'm')
305 else:
306 # We've update-merged a locally modified file, so
307 # we set the dirstate to emulate a normal checkout
308 # of that file some time in the past. Thus our
309 # merge will appear as a normal local file
310 # modification.
311 fl = repo.file(f)
312 f_len = fl.size(fl.rev(other))
313 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
314
315 if show_stats:
316 stats = ((len(get), _("updated")),
317 (len(merge) - len(unresolved), _("merged")),
318 (len(remove), _("removed")),
319 (len(unresolved), _("unresolved")))
320 note = ", ".join([_("%d files %s") % s for s in stats])
321 repo.ui.status("%s\n" % note)
322 if not partial:
323 if branchmerge:
324 if unresolved:
325 repo.ui.status(_("There are unresolved merges,"
326 " you can redo the full merge using:\n"
327 " hg update -C %s\n"
328 " hg merge %s\n"
329 % (repo.changelog.rev(p1),
330 repo.changelog.rev(p2))))
331 elif remind:
332 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
333 elif unresolved:
334 repo.ui.status(_("There are unresolved merges with"
335 " locally modified files.\n"))
336
337 repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved))
338 return len(unresolved)
339
This diff has been collapsed as it changes many lines, (539 lines changed) Show them Hide them
@@ -0,0 +1,539 b''
1 # patch.py - patch file parsing routines
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from demandload import demandload
9 from i18n import gettext as _
10 from node import *
11 demandload(globals(), "cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
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
36 def extract(ui, fileobj):
37 '''extract patch from data read from fileobj.
38
39 patch can be normal patch or contained in email message.
40
41 return tuple (filename, message, user, date). any item in returned
42 tuple can be None. if filename is None, fileobj did not contain
43 patch. caller must unlink filename when done.'''
44
45 # attempt to detect the start of a patch
46 # (this heuristic is borrowed from quilt)
47 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
48 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
49 '(---|\*\*\*)[ \t])', re.MULTILINE)
50
51 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
52 tmpfp = os.fdopen(fd, 'w')
53 try:
54 hgpatch = False
55
56 msg = email.Parser.Parser().parse(fileobj)
57
58 message = msg['Subject']
59 user = msg['From']
60 # should try to parse msg['Date']
61 date = None
62
63 if message:
64 message = message.replace('\n\t', ' ')
65 ui.debug('Subject: %s\n' % message)
66 if user:
67 ui.debug('From: %s\n' % user)
68 diffs_seen = 0
69 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
70
71 for part in msg.walk():
72 content_type = part.get_content_type()
73 ui.debug('Content-Type: %s\n' % content_type)
74 if content_type not in ok_types:
75 continue
76 payload = part.get_payload(decode=True)
77 m = diffre.search(payload)
78 if m:
79 ui.debug(_('found patch at byte %d\n') % m.start(0))
80 diffs_seen += 1
81 cfp = cStringIO.StringIO()
82 if message:
83 cfp.write(message)
84 cfp.write('\n')
85 for line in payload[:m.start(0)].splitlines():
86 if line.startswith('# HG changeset patch'):
87 ui.debug(_('patch generated by hg export\n'))
88 hgpatch = True
89 # drop earlier commit message content
90 cfp.seek(0)
91 cfp.truncate()
92 elif hgpatch:
93 if line.startswith('# User '):
94 user = line[7:]
95 ui.debug('From: %s\n' % user)
96 elif line.startswith("# Date "):
97 date = line[7:]
98 if not line.startswith('# '):
99 cfp.write(line)
100 cfp.write('\n')
101 message = cfp.getvalue()
102 if tmpfp:
103 tmpfp.write(payload)
104 if not payload.endswith('\n'):
105 tmpfp.write('\n')
106 elif not diffs_seen and message and content_type == 'text/plain':
107 message += '\n' + payload
108 except:
109 tmpfp.close()
110 os.unlink(tmpname)
111 raise
112
113 tmpfp.close()
114 if not diffs_seen:
115 os.unlink(tmpname)
116 return None, message, user, date
117 return tmpname, message, user, date
118
119 def readgitpatch(patchname):
120 """extract git-style metadata about patches from <patchname>"""
121 class gitpatch:
122 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
123 def __init__(self, path):
124 self.path = path
125 self.oldpath = None
126 self.mode = None
127 self.op = 'MODIFY'
128 self.copymod = False
129 self.lineno = 0
130
131 # Filter patch for git information
132 gitre = re.compile('diff --git a/(.*) b/(.*)')
133 pf = file(patchname)
134 gp = None
135 gitpatches = []
136 # Can have a git patch with only metadata, causing patch to complain
137 dopatch = False
138
139 lineno = 0
140 for line in pf:
141 lineno += 1
142 if line.startswith('diff --git'):
143 m = gitre.match(line)
144 if m:
145 if gp:
146 gitpatches.append(gp)
147 src, dst = m.group(1,2)
148 gp = gitpatch(dst)
149 gp.lineno = lineno
150 elif gp:
151 if line.startswith('--- '):
152 if gp.op in ('COPY', 'RENAME'):
153 gp.copymod = True
154 dopatch = 'filter'
155 gitpatches.append(gp)
156 gp = None
157 if not dopatch:
158 dopatch = True
159 continue
160 if line.startswith('rename from '):
161 gp.op = 'RENAME'
162 gp.oldpath = line[12:].rstrip()
163 elif line.startswith('rename to '):
164 gp.path = line[10:].rstrip()
165 elif line.startswith('copy from '):
166 gp.op = 'COPY'
167 gp.oldpath = line[10:].rstrip()
168 elif line.startswith('copy to '):
169 gp.path = line[8:].rstrip()
170 elif line.startswith('deleted file'):
171 gp.op = 'DELETE'
172 elif line.startswith('new file mode '):
173 gp.op = 'ADD'
174 gp.mode = int(line.rstrip()[-3:], 8)
175 elif line.startswith('new mode '):
176 gp.mode = int(line.rstrip()[-3:], 8)
177 if gp:
178 gitpatches.append(gp)
179
180 if not gitpatches:
181 dopatch = True
182
183 return (dopatch, gitpatches)
184
185 def dogitpatch(patchname, gitpatches):
186 """Preprocess git patch so that vanilla patch can handle it"""
187 pf = file(patchname)
188 pfline = 1
189
190 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
191 tmpfp = os.fdopen(fd, 'w')
192
193 try:
194 for i in range(len(gitpatches)):
195 p = gitpatches[i]
196 if not p.copymod:
197 continue
198
199 copyfile(p.oldpath, p.path)
200
201 # rewrite patch hunk
202 while pfline < p.lineno:
203 tmpfp.write(pf.readline())
204 pfline += 1
205 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
206 line = pf.readline()
207 pfline += 1
208 while not line.startswith('--- a/'):
209 tmpfp.write(line)
210 line = pf.readline()
211 pfline += 1
212 tmpfp.write('--- a/%s\n' % p.path)
213
214 line = pf.readline()
215 while line:
216 tmpfp.write(line)
217 line = pf.readline()
218 except:
219 tmpfp.close()
220 os.unlink(patchname)
221 raise
222
223 tmpfp.close()
224 return patchname
225
226 def patch(patchname, ui, strip=1, cwd=None):
227 """apply the patch <patchname> to the working directory.
228 a list of patched files is returned"""
229
230 (dopatch, gitpatches) = readgitpatch(patchname)
231
232 files = {}
233 fuzz = False
234 if dopatch:
235 if dopatch == 'filter':
236 patchname = dogitpatch(patchname, gitpatches)
237 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
238 args = []
239 if cwd:
240 args.append('-d %s' % util.shellquote(cwd))
241 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
242 util.shellquote(patchname)))
243
244 if dopatch == 'filter':
245 False and os.unlink(patchname)
246
247 for line in fp:
248 line = line.rstrip()
249 ui.note(line + '\n')
250 if line.startswith('patching file '):
251 pf = util.parse_patch_output(line)
252 printed_file = False
253 files.setdefault(pf, (None, None))
254 elif line.find('with fuzz') >= 0:
255 fuzz = True
256 if not printed_file:
257 ui.warn(pf + '\n')
258 printed_file = True
259 ui.warn(line + '\n')
260 elif line.find('saving rejects to file') >= 0:
261 ui.warn(line + '\n')
262 elif line.find('FAILED') >= 0:
263 if not printed_file:
264 ui.warn(pf + '\n')
265 printed_file = True
266 ui.warn(line + '\n')
267
268 code = fp.close()
269 if code:
270 raise util.Abort(_("patch command failed: %s") %
271 util.explain_exit(code)[0])
272
273 for gp in gitpatches:
274 files[gp.path] = (gp.op, gp)
275
276 return (files, fuzz)
277
278 def diffopts(ui, opts={}):
279 return mdiff.diffopts(
280 text=opts.get('text'),
281 git=(opts.get('git') or
282 ui.configbool('diff', 'git', None)),
283 showfunc=(opts.get('show_function') or
284 ui.configbool('diff', 'showfunc', None)),
285 ignorews=(opts.get('ignore_all_space') or
286 ui.configbool('diff', 'ignorews', None)),
287 ignorewsamount=(opts.get('ignore_space_change') or
288 ui.configbool('diff', 'ignorewsamount', None)),
289 ignoreblanklines=(opts.get('ignore_blank_lines') or
290 ui.configbool('diff', 'ignoreblanklines', None)))
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
331 def diff(repo, node1=None, node2=None, files=None, match=util.always,
332 fp=None, changes=None, opts=None):
333 '''print diff of changes to files between two nodes, or node and
334 working directory.
335
336 if node1 is None, use first dirstate parent instead.
337 if node2 is None, compare node1 with working directory.'''
338
339 if opts is None:
340 opts = mdiff.defaultopts
341 if fp is None:
342 fp = repo.ui
343
344 if not node1:
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
363 # reading the data for node1 early allows it to play nicely
364 # with repo.status and the revlog cache.
365 change = getchangelog(node1)
366 mmap = getmanifest(change[0])
367 date1 = util.datestr(change[2])
368
369 if not changes:
370 changes = repo.status(node1, node2, files, match=match)[:5]
371 modified, added, removed, deleted, unknown = changes
372 if files:
373 def filterfiles(filters):
374 l = [x for x in filters if x in files]
375
376 for t in files:
377 if not t.endswith("/"):
378 t += "/"
379 l += [x for x in filters if x.startswith(t)]
380 return l
381
382 modified, added, removed = map(filterfiles, (modified, added, removed))
383
384 if not modified and not added and not removed:
385 return
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
403 if node2:
404 change = getchangelog(node2)
405 mmap2 = getmanifest(change[0])
406 _date2 = util.datestr(change[2])
407 def date2(f):
408 return _date2
409 def read(f):
410 return getfile(f).read(mmap2[f])
411 def renamed(f):
412 return renamedbetween(f, node1, node2)
413 else:
414 tz = util.makedate()[1]
415 _date2 = util.datestr()
416 def date2(f):
417 try:
418 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
419 except OSError, err:
420 if err.errno != errno.ENOENT: raise
421 return _date2
422 def read(f):
423 return repo.wread(f)
424 def renamed(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
437
438 if repo.ui.quiet:
439 r = None
440 else:
441 hexfunc = repo.ui.verbose and hex or short
442 r = [hexfunc(node) for node in [node1, node2] if node]
443
444 if opts.git:
445 copied = {}
446 for f in added:
447 src = renamed(f)
448 if src:
449 copied[f] = src
450 srcs = [x[1][0] for x in copied.items()]
451
452 all = modified + added + removed
453 all.sort()
454 for f in all:
455 to = None
456 tn = None
457 dodiff = True
458 if f in mmap:
459 to = getfile(f).read(mmap[f])
460 if f not in removed:
461 tn = read(f)
462 if opts.git:
463 def gitmode(x):
464 return x and '100755' or '100644'
465 def addmodehdr(header, omode, nmode):
466 if omode != nmode:
467 header.append('old mode %s\n' % omode)
468 header.append('new mode %s\n' % nmode)
469
470 a, b = f, f
471 header = []
472 if f in added:
473 if node2:
474 mode = gitmode(mmap2.execf(f))
475 else:
476 mode = gitmode(util.is_exec(repo.wjoin(f), None))
477 if f in copied:
478 a, arev = copied[f]
479 omode = gitmode(mmap.execf(a))
480 addmodehdr(header, omode, mode)
481 op = a in removed and 'rename' or 'copy'
482 header.append('%s from %s\n' % (op, a))
483 header.append('%s to %s\n' % (op, f))
484 to = getfile(a).read(arev)
485 else:
486 header.append('new file mode %s\n' % mode)
487 elif f in removed:
488 if f in srcs:
489 dodiff = False
490 else:
491 mode = gitmode(mmap.execf(f))
492 header.append('deleted file mode %s\n' % mode)
493 else:
494 omode = gitmode(mmap.execf(f))
495 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
496 addmodehdr(header, omode, nmode)
497 r = None
498 if dodiff:
499 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
500 fp.write(''.join(header))
501 if dodiff:
502 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
503
504 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
505 opts=None):
506 '''export changesets as hg patches.'''
507
508 total = len(revs)
509 revwidth = max(map(len, revs))
510
511 def single(node, seqno, fp):
512 parents = [p for p in repo.changelog.parents(node) if p != nullid]
513 if switch_parent:
514 parents.reverse()
515 prev = (parents and parents[0]) or nullid
516 change = repo.changelog.read(node)
517
518 if not fp:
519 fp = cmdutil.make_file(repo, template, node, total=total,
520 seqno=seqno, revwidth=revwidth)
521 if fp not in (sys.stdout, repo.ui):
522 repo.ui.note("%s\n" % fp.name)
523
524 fp.write("# HG changeset patch\n")
525 fp.write("# User %s\n" % change[1])
526 fp.write("# Date %d %d\n" % change[2])
527 fp.write("# Node ID %s\n" % hex(node))
528 fp.write("# Parent %s\n" % hex(prev))
529 if len(parents) > 1:
530 fp.write("# Parent %s\n" % hex(parents[1]))
531 fp.write(change[4].rstrip())
532 fp.write("\n\n")
533
534 diff(repo, prev, node, fp=fp, opts=opts)
535 if fp not in (sys.stdout, repo.ui):
536 fp.close()
537
538 for seqno, cset in enumerate(revs):
539 single(cset, seqno, fp)
@@ -0,0 +1,34 b''
1 # strutil.py - string utilities for Mercurial
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 def findall(haystack, needle, start=0, end=None):
9 if end is None:
10 end = len(haystack)
11 if end < 0:
12 end += len(haystack)
13 if start < 0:
14 start += len(haystack)
15 while start < end:
16 c = haystack.find(needle, start, end)
17 if c == -1:
18 break
19 yield c
20 start = c + 1
21
22 def rfindall(haystack, needle, start=0, end=None):
23 if end is None:
24 end = len(haystack)
25 if end < 0:
26 end += len(haystack)
27 if start < 0:
28 start += len(haystack)
29 while end >= 0:
30 c = haystack.rfind(needle, start, end)
31 if c == -1:
32 break
33 yield c
34 end = c - 1
@@ -0,0 +1,200 b''
1 # verify.py - repository integrity checking for Mercurial
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from node import *
9 from i18n import gettext as _
10 import revlog, mdiff
11
12 def verify(repo):
13 filelinkrevs = {}
14 filenodes = {}
15 changesets = revisions = files = 0
16 errors = [0]
17 warnings = [0]
18 neededmanifests = {}
19
20 def err(msg):
21 repo.ui.warn(msg + "\n")
22 errors[0] += 1
23
24 def warn(msg):
25 repo.ui.warn(msg + "\n")
26 warnings[0] += 1
27
28 def checksize(obj, name):
29 d = obj.checksize()
30 if d[0]:
31 err(_("%s data length off by %d bytes") % (name, d[0]))
32 if d[1]:
33 err(_("%s index contains %d extra bytes") % (name, d[1]))
34
35 def checkversion(obj, name):
36 if obj.version != revlog.REVLOGV0:
37 if not revlogv1:
38 warn(_("warning: `%s' uses revlog format 1") % name)
39 elif revlogv1:
40 warn(_("warning: `%s' uses revlog format 0") % name)
41
42 revlogv1 = repo.revlogversion != revlog.REVLOGV0
43 if repo.ui.verbose or revlogv1 != repo.revlogv1:
44 repo.ui.status(_("repository uses revlog format %d\n") %
45 (revlogv1 and 1 or 0))
46
47 seen = {}
48 repo.ui.status(_("checking changesets\n"))
49 checksize(repo.changelog, "changelog")
50
51 for i in range(repo.changelog.count()):
52 changesets += 1
53 n = repo.changelog.node(i)
54 l = repo.changelog.linkrev(n)
55 if l != i:
56 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
57 if n in seen:
58 err(_("duplicate changeset at revision %d") % i)
59 seen[n] = 1
60
61 for p in repo.changelog.parents(n):
62 if p not in repo.changelog.nodemap:
63 err(_("changeset %s has unknown parent %s") %
64 (short(n), short(p)))
65 try:
66 changes = repo.changelog.read(n)
67 except KeyboardInterrupt:
68 repo.ui.warn(_("interrupted"))
69 raise
70 except Exception, inst:
71 err(_("unpacking changeset %s: %s") % (short(n), inst))
72 continue
73
74 neededmanifests[changes[0]] = n
75
76 for f in changes[3]:
77 filelinkrevs.setdefault(f, []).append(i)
78
79 seen = {}
80 repo.ui.status(_("checking manifests\n"))
81 checkversion(repo.manifest, "manifest")
82 checksize(repo.manifest, "manifest")
83
84 for i in range(repo.manifest.count()):
85 n = repo.manifest.node(i)
86 l = repo.manifest.linkrev(n)
87
88 if l < 0 or l >= repo.changelog.count():
89 err(_("bad manifest link (%d) at revision %d") % (l, i))
90
91 if n in neededmanifests:
92 del neededmanifests[n]
93
94 if n in seen:
95 err(_("duplicate manifest at revision %d") % i)
96
97 seen[n] = 1
98
99 for p in repo.manifest.parents(n):
100 if p not in repo.manifest.nodemap:
101 err(_("manifest %s has unknown parent %s") %
102 (short(n), short(p)))
103
104 try:
105 delta = mdiff.patchtext(repo.manifest.delta(n))
106 except KeyboardInterrupt:
107 repo.ui.warn(_("interrupted"))
108 raise
109 except Exception, inst:
110 err(_("unpacking manifest %s: %s") % (short(n), inst))
111 continue
112
113 try:
114 ff = [ l.split('\0') for l in delta.splitlines() ]
115 for f, fn in ff:
116 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
117 except (ValueError, TypeError), inst:
118 err(_("broken delta in manifest %s: %s") % (short(n), inst))
119
120 repo.ui.status(_("crosschecking files in changesets and manifests\n"))
121
122 for m, c in neededmanifests.items():
123 err(_("Changeset %s refers to unknown manifest %s") %
124 (short(m), short(c)))
125 del neededmanifests
126
127 for f in filenodes:
128 if f not in filelinkrevs:
129 err(_("file %s in manifest but not in changesets") % f)
130
131 for f in filelinkrevs:
132 if f not in filenodes:
133 err(_("file %s in changeset but not in manifest") % f)
134
135 repo.ui.status(_("checking files\n"))
136 ff = filenodes.keys()
137 ff.sort()
138 for f in ff:
139 if f == "/dev/null":
140 continue
141 files += 1
142 if not f:
143 err(_("file without name in manifest %s") % short(n))
144 continue
145 fl = repo.file(f)
146 checkversion(fl, f)
147 checksize(fl, f)
148
149 nodes = {nullid: 1}
150 seen = {}
151 for i in range(fl.count()):
152 revisions += 1
153 n = fl.node(i)
154
155 if n in seen:
156 err(_("%s: duplicate revision %d") % (f, i))
157 if n not in filenodes[f]:
158 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
159 else:
160 del filenodes[f][n]
161
162 flr = fl.linkrev(n)
163 if flr not in filelinkrevs.get(f, []):
164 err(_("%s:%s points to unexpected changeset %d")
165 % (f, short(n), flr))
166 else:
167 filelinkrevs[f].remove(flr)
168
169 # verify contents
170 try:
171 t = fl.read(n)
172 except KeyboardInterrupt:
173 repo.ui.warn(_("interrupted"))
174 raise
175 except Exception, inst:
176 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
177
178 # verify parents
179 (p1, p2) = fl.parents(n)
180 if p1 not in nodes:
181 err(_("file %s:%s unknown parent 1 %s") %
182 (f, short(n), short(p1)))
183 if p2 not in nodes:
184 err(_("file %s:%s unknown parent 2 %s") %
185 (f, short(n), short(p1)))
186 nodes[n] = 1
187
188 # cross-check
189 for node in filenodes[f]:
190 err(_("node %s in manifests not in %s") % (hex(node), f))
191
192 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
193 (files, changesets, revisions))
194
195 if warnings[0]:
196 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
197 if errors[0]:
198 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
199 return 1
200
@@ -0,0 +1,38 b''
1 #header#
2 <title>#repo|escape#: shortlog</title>
3 <link rel="alternate" type="application/rss+xml"
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 </head>
6 <body>
7
8 <div class="buttons">
9 <a href="?cl=#rev#">changelog</a>
10 <a href="?cmd=tags">tags</a>
11 <a href="?mf=#manifest|short#;path=/">manifest</a>
12 #archives%archiveentry#
13 <a type="application/rss+xml" href="?style=rss">rss</a>
14 </div>
15
16 <h2>shortlog for #repo|escape#</h2>
17
18 <form action="#">
19 <p>
20 <label for="search1">search:</label>
21 <input type="hidden" name="cmd" value="changelog">
22 <input name="rev" id="search1" type="text" size="30">
23 navigate: <small class="navigate">#changenav%navshortentry#</small>
24 </p>
25 </form>
26
27 #entries%shortlogentry#
28
29 <form action="#">
30 <p>
31 <label for="search2">search:</label>
32 <input type="hidden" name="cmd" value="changelog">
33 <input name="rev" id="search2" type="text" size="30">
34 navigate: <small class="navigate">#changenav%navshortentry#</small>
35 </p>
36 </form>
37
38 #footer#
@@ -0,0 +1,7 b''
1 <table class="slogEntry parity#parity#">
2 <tr>
3 <td class="age">#date|age#</td>
4 <td class="author">#author|obfuscate#</td>
5 <td class="node"><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></td>
6 </tr>
7 </table>
@@ -0,0 +1,22 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6 cat > $HGTMP/false <<EOF
7 #!/bin/sh
8 exit 1
9 EOF
10 chmod +x $HGTMP/false
11
12 hg init foo
13 cd foo
14 echo foo > foo
15 hg add foo
16
17 # mq may keep a reference to the repository so __del__ will not be called
18 # and .hg/journal.dirstate will not be deleted:
19 HGEDITOR=$HGTMP/false hg ci
20 HGEDITOR=$HGTMP/false hg ci
21
22 exit 0
@@ -0,0 +1,6 b''
1 abort: edit failed: false exited with status 1
2 transaction abort!
3 rollback completed
4 abort: edit failed: false exited with status 1
5 transaction abort!
6 rollback completed
@@ -0,0 +1,23 b''
1 #!/bin/sh
2
3 echo % init
4 hg init
5
6 echo % commit
7 echo 'a' > a
8 hg ci -A -m test -u nobody -d '1 0'
9
10 echo % annotate -c
11 hg annotate -c a
12
13 echo % annotate -d
14 hg annotate -d a
15
16 echo % annotate -n
17 hg annotate -n a
18
19 echo % annotate -u
20 hg annotate -u a
21
22 echo % annotate -cdnu
23 hg annotate -cdnu a
@@ -0,0 +1,13 b''
1 % init
2 % commit
3 adding a
4 % annotate -c
5 8435f90966e4: a
6 % annotate -d
7 Thu Jan 01 00:00:01 1970 +0000: a
8 % annotate -n
9 0: a
10 % annotate -u
11 nobody: a
12 % annotate -cdnu
13 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a
@@ -0,0 +1,37 b''
1 #!/bin/sh
2
3 set -e
4
5 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
6 echo "[extensions]" >> $HGTMP/.hgrc
7 echo "hbisect=" >> $HGTMP/.hgrc
8
9 echo % init
10 hg init
11
12 echo % committing changes
13 count=0
14 echo > a
15 while test $count -lt 32 ; do
16 echo 'a' >> a
17 test $count -eq 0 && hg add
18 hg ci -m "msg $count" -d "$count 0"
19 echo % committed changeset $count
20 count=`expr $count + 1`
21 done
22
23 echo % log
24 hg log
25
26 echo % hg up -C
27 hg up -C
28
29 echo % bisect test
30 hg bisect init
31 hg bisect bad
32 hg bisect good 1
33 hg bisect good
34 hg bisect good
35 hg bisect good
36 hg bisect bad
37 hg bisect good
@@ -0,0 +1,216 b''
1 % init
2 % committing changes
3 adding a
4 % committed changeset 0
5 % committed changeset 1
6 % committed changeset 2
7 % committed changeset 3
8 % committed changeset 4
9 % committed changeset 5
10 % committed changeset 6
11 % committed changeset 7
12 % committed changeset 8
13 % committed changeset 9
14 % committed changeset 10
15 % committed changeset 11
16 % committed changeset 12
17 % committed changeset 13
18 % committed changeset 14
19 % committed changeset 15
20 % committed changeset 16
21 % committed changeset 17
22 % committed changeset 18
23 % committed changeset 19
24 % committed changeset 20
25 % committed changeset 21
26 % committed changeset 22
27 % committed changeset 23
28 % committed changeset 24
29 % committed changeset 25
30 % committed changeset 26
31 % committed changeset 27
32 % committed changeset 28
33 % committed changeset 29
34 % committed changeset 30
35 % committed changeset 31
36 % log
37 changeset: 31:58c80a7c8a40
38 tag: tip
39 user: test
40 date: Thu Jan 01 00:00:31 1970 +0000
41 summary: msg 31
42
43 changeset: 30:ed2d2f24b11c
44 user: test
45 date: Thu Jan 01 00:00:30 1970 +0000
46 summary: msg 30
47
48 changeset: 29:b5bd63375ab9
49 user: test
50 date: Thu Jan 01 00:00:29 1970 +0000
51 summary: msg 29
52
53 changeset: 28:8e0c2264c8af
54 user: test
55 date: Thu Jan 01 00:00:28 1970 +0000
56 summary: msg 28
57
58 changeset: 27:288867a866e9
59 user: test
60 date: Thu Jan 01 00:00:27 1970 +0000
61 summary: msg 27
62
63 changeset: 26:3efc6fd51aeb
64 user: test
65 date: Thu Jan 01 00:00:26 1970 +0000
66 summary: msg 26
67
68 changeset: 25:02a84173a97a
69 user: test
70 date: Thu Jan 01 00:00:25 1970 +0000
71 summary: msg 25
72
73 changeset: 24:10e0acd3809e
74 user: test
75 date: Thu Jan 01 00:00:24 1970 +0000
76 summary: msg 24
77
78 changeset: 23:5ec79163bff4
79 user: test
80 date: Thu Jan 01 00:00:23 1970 +0000
81 summary: msg 23
82
83 changeset: 22:06c7993750ce
84 user: test
85 date: Thu Jan 01 00:00:22 1970 +0000
86 summary: msg 22
87
88 changeset: 21:e5db6aa3fe2a
89 user: test
90 date: Thu Jan 01 00:00:21 1970 +0000
91 summary: msg 21
92
93 changeset: 20:7128fb4fdbc9
94 user: test
95 date: Thu Jan 01 00:00:20 1970 +0000
96 summary: msg 20
97
98 changeset: 19:52798545b482
99 user: test
100 date: Thu Jan 01 00:00:19 1970 +0000
101 summary: msg 19
102
103 changeset: 18:86977a90077e
104 user: test
105 date: Thu Jan 01 00:00:18 1970 +0000
106 summary: msg 18
107
108 changeset: 17:03515f4a9080
109 user: test
110 date: Thu Jan 01 00:00:17 1970 +0000
111 summary: msg 17
112
113 changeset: 16:a2e6ea4973e9
114 user: test
115 date: Thu Jan 01 00:00:16 1970 +0000
116 summary: msg 16
117
118 changeset: 15:e7fa0811edb0
119 user: test
120 date: Thu Jan 01 00:00:15 1970 +0000
121 summary: msg 15
122
123 changeset: 14:ce8f0998e922
124 user: test
125 date: Thu Jan 01 00:00:14 1970 +0000
126 summary: msg 14
127
128 changeset: 13:9d7d07bc967c
129 user: test
130 date: Thu Jan 01 00:00:13 1970 +0000
131 summary: msg 13
132
133 changeset: 12:1941b52820a5
134 user: test
135 date: Thu Jan 01 00:00:12 1970 +0000
136 summary: msg 12
137
138 changeset: 11:7b4cd9578619
139 user: test
140 date: Thu Jan 01 00:00:11 1970 +0000
141 summary: msg 11
142
143 changeset: 10:7c5eff49a6b6
144 user: test
145 date: Thu Jan 01 00:00:10 1970 +0000
146 summary: msg 10
147
148 changeset: 9:eb44510ef29a
149 user: test
150 date: Thu Jan 01 00:00:09 1970 +0000
151 summary: msg 9
152
153 changeset: 8:453eb4dba229
154 user: test
155 date: Thu Jan 01 00:00:08 1970 +0000
156 summary: msg 8
157
158 changeset: 7:03750880c6b5
159 user: test
160 date: Thu Jan 01 00:00:07 1970 +0000
161 summary: msg 7
162
163 changeset: 6:a3d5c6fdf0d3
164 user: test
165 date: Thu Jan 01 00:00:06 1970 +0000
166 summary: msg 6
167
168 changeset: 5:7874a09ea728
169 user: test
170 date: Thu Jan 01 00:00:05 1970 +0000
171 summary: msg 5
172
173 changeset: 4:9b2ba8336a65
174 user: test
175 date: Thu Jan 01 00:00:04 1970 +0000
176 summary: msg 4
177
178 changeset: 3:b53bea5e2fcb
179 user: test
180 date: Thu Jan 01 00:00:03 1970 +0000
181 summary: msg 3
182
183 changeset: 2:db07c04beaca
184 user: test
185 date: Thu Jan 01 00:00:02 1970 +0000
186 summary: msg 2
187
188 changeset: 1:5cd978ea5149
189 user: test
190 date: Thu Jan 01 00:00:01 1970 +0000
191 summary: msg 1
192
193 changeset: 0:b99c7b9c8e11
194 user: test
195 date: Thu Jan 01 00:00:00 1970 +0000
196 summary: msg 0
197
198 % hg up -C
199 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 % bisect test
201 Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests)
204 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 Testing changeset 27:288867a866e9 (8 changesets remaining, ~3 tests)
206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests)
208 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 Testing changeset 28:8e0c2264c8af (2 changesets remaining, ~1 tests)
210 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
211 The first bad revision is:
212 changeset: 29:b5bd63375ab9
213 user: test
214 date: Thu Jan 01 00:00:29 1970 +0000
215 summary: msg 29
216
@@ -0,0 +1,27 b''
1 #!/bin/sh
2
3 hg init
4
5 mkdir alpha
6 touch alpha/one
7 mkdir beta
8 touch beta/two
9
10 hg add alpha/one beta/two
11 hg ci -m "start" -d "1000000 0"
12
13 echo 1 > alpha/one
14 echo 2 > beta/two
15
16 echo EVERYTHING
17 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
18 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
19
20 echo BETA ONLY
21 hg diff beta | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
22 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
23
24 echo INSIDE BETA
25 cd beta
26 hg diff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
27 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
@@ -0,0 +1,23 b''
1 EVERYTHING
2 diff -r ec612a6291f1 alpha/one
3 --- a/alpha/one
4 +++ b/alpha/one
5 @@ -0,0 +1,1 @@
6 +1
7 diff -r ec612a6291f1 beta/two
8 --- a/beta/two
9 +++ b/beta/two
10 @@ -0,0 +1,1 @@
11 +2
12 BETA ONLY
13 diff -r ec612a6291f1 beta/two
14 --- a/beta/two
15 +++ b/beta/two
16 @@ -0,0 +1,1 @@
17 +2
18 INSIDE BETA
19 diff -r ec612a6291f1 beta/two
20 --- a/beta/two
21 +++ b/beta/two
22 @@ -0,0 +1,1 @@
23 +2
@@ -0,0 +1,30 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "extdiff=" >> $HGTMP/.hgrc
6
7 hg init a
8 cd a
9 echo a > a
10 hg add
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
16
17 echo "[extdiff]" >> $HGTMP/.hgrc
18 echo "cmd.falabala=echo" >> $HGTMP/.hgrc
19 echo "opts.falabala=diffing" >> $HGTMP/.hgrc
20
21 hg falabala
22
23 hg help falabala
24
25 hg ci -d '0 0' -mtest1
26
27 echo b >> a
28 hg ci -d '1 0' -mtest2
29
30 hg falabala -r 0:1 || echo "diff-like tools yield a non-zero exit code"
@@ -0,0 +1,32 b''
1 adding a
2 making snapshot of 0 files from rev 000000000000
3 making snapshot of 1 files from working dir
4 diff -Nr a.000000000000/a a/a
5 0a1
6 > a
7 making snapshot of 0 files from rev 000000000000
8 making snapshot of 1 files from working dir
9 diffing a.000000000000 a
10 hg falabala [OPT]... [FILE]...
11
12 use 'echo' to diff repository (or selected files)
13
14 Show differences between revisions for the specified
15 files, using the 'echo' program.
16
17 When two revision arguments are given, then changes are
18 shown between those revisions. If only one revision is
19 specified then that revision is compared to the working
20 directory, and, when no revisions are specified, the
21 working directory files are compared to its parent.
22
23 options:
24
25 -o --option pass option to comparison program
26 -r --rev revision
27 -I --include include names matching the given patterns
28 -X --exclude exclude names matching the given patterns
29 making snapshot of 1 files from rev e27a2475d60a
30 making snapshot of 1 files from rev 5e49ec8d3f05
31 diffing a.e27a2475d60a a.5e49ec8d3f05
32 diff-like tools yield a non-zero exit code
@@ -0,0 +1,25 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "fetch=" >> $HGTMP/.hgrc
6
7 hg init a
8 echo a > a/a
9 hg --cwd a commit -d '1 0' -Ama
10
11 hg clone a b
12 hg clone a c
13
14 echo b > a/b
15 hg --cwd a commit -d '2 0' -Amb
16 hg --cwd a parents -q
17
18 echo % should pull one change
19 hg --cwd b fetch ../a
20 hg --cwd b parents -q
21
22 echo c > c/c
23 hg --cwd c commit -d '3 0' -Amc
24 hg --cwd c fetch -d '4 0' -m 'automated merge' ../a
25 ls c
@@ -0,0 +1,27 b''
1 adding a
2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 adding b
5 1:97d72e5f12c7
6 % should pull one change
7 pulling from ../a
8 searching for changes
9 adding changesets
10 adding manifests
11 adding file changes
12 added 1 changesets with 1 changes to 1 files
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 1:97d72e5f12c7
15 adding c
16 pulling from ../a
17 searching for changes
18 adding changesets
19 adding manifests
20 adding file changes
21 added 1 changesets with 1 changes to 1 files (+1 heads)
22 merging with new head 2:97d72e5f12c7
23 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 new changeset 3:cd3a41621cf0 merges remote changes with local
25 a
26 b
27 c
@@ -0,0 +1,52 b''
1 #!/bin/sh
2
3 hg init a
4 cd a
5
6 echo start > start
7 hg ci -Amstart -d '0 0'
8 echo new > new
9 hg ci -Amnew -d '0 0'
10 echo '% new file'
11 hg diff --git -r 0 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
12 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
13
14 hg cp new copy
15 hg ci -mcopy -d '0 0'
16 echo '% copy'
17 hg diff --git -r 1:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
18 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
19
20 hg mv copy rename
21 hg ci -mrename -d '0 0'
22 echo '% rename'
23 hg diff --git -r 2:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
24 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
25
26 hg rm rename
27 hg ci -mdelete -d '0 0'
28 echo '% delete'
29 hg diff --git -r 3:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
30 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
31
32 cat > src <<EOF
33 1
34 2
35 3
36 4
37 5
38 EOF
39 hg ci -Amsrc -d '0 0'
40 chmod +x src
41 hg ci -munexec -d '0 0'
42 echo '% chmod 644'
43 hg diff --git -r 5:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
44 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
45
46 hg mv src dst
47 chmod -x dst
48 echo a >> dst
49 hg ci -mrenamemod -d '0 0'
50 echo '% rename+mod+chmod'
51 hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
52 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
@@ -0,0 +1,42 b''
1 adding start
2 adding new
3 % new file
4 diff --git a/new b/new
5 new file mode 100644
6 --- /dev/null
7 +++ b/new
8 @@ -0,0 +1,1 @@
9 +new
10 % copy
11 diff --git a/new b/copy
12 copy from new
13 copy to copy
14 % rename
15 diff --git a/copy b/rename
16 rename from copy
17 rename to rename
18 % delete
19 diff --git a/rename b/rename
20 deleted file mode 100644
21 --- a/rename
22 +++ /dev/null
23 @@ -1,1 +0,0 @@
24 -new
25 adding src
26 % chmod 644
27 diff --git a/src b/src
28 old mode 100644
29 new mode 100755
30 % rename+mod+chmod
31 diff --git a/src b/dst
32 old mode 100755
33 new mode 100644
34 rename from src
35 rename to dst
36 --- a/dst
37 +++ b/dst
38 @@ -3,3 +3,4 @@ 3
39 3
40 4
41 5
42 +a
@@ -0,0 +1,122 b''
1 #!/bin/sh
2
3 hg init a
4 cd a
5
6 echo % new file
7 hg import -mnew - <<EOF
8 diff --git a/new b/new
9 new file mode 100644
10 index 0000000..7898192
11 --- /dev/null
12 +++ b/new
13 @@ -0,0 +1 @@
14 +a
15 EOF
16
17 echo % chmod +x
18 hg import -msetx - <<EOF
19 diff --git a/new b/new
20 old mode 100644
21 new mode 100755
22 EOF
23
24 test -x new || echo failed
25
26 echo % copy
27 hg import -mcopy - <<EOF
28 diff --git a/new b/copy
29 old mode 100755
30 new mode 100644
31 similarity index 100%
32 copy from new
33 copy to copy
34 diff --git a/new b/copyx
35 similarity index 100%
36 copy from new
37 copy to copyx
38 EOF
39
40 test -f copy -a ! -x copy || echo failed
41 test -x copyx || echo failed
42 cat copy
43 hg cat copy
44
45 echo % rename
46 hg import -mrename - <<EOF
47 diff --git a/copy b/rename
48 similarity index 100%
49 rename from copy
50 rename to rename
51 EOF
52
53 hg locate
54
55 echo % delete
56 hg import -mdelete - <<EOF
57 diff --git a/copyx b/copyx
58 deleted file mode 100755
59 index 7898192..0000000
60 --- a/copyx
61 +++ /dev/null
62 @@ -1 +0,0 @@
63 -a
64 EOF
65
66 hg locate
67 test -f copyx && echo failed || true
68
69 echo % regular diff
70 hg import -mregular - <<EOF
71 diff --git a/rename b/rename
72 index 7898192..72e1fe3 100644
73 --- a/rename
74 +++ b/rename
75 @@ -1 +1,5 @@
76 a
77 +a
78 +a
79 +a
80 +a
81 EOF
82
83 echo % copy and modify
84 hg import -mcopymod - <<EOF
85 diff --git a/rename b/copy2
86 similarity index 80%
87 copy from rename
88 copy to copy2
89 index 72e1fe3..b53c148 100644
90 --- a/rename
91 +++ b/copy2
92 @@ -1,5 +1,5 @@
93 a
94 a
95 -a
96 +b
97 a
98 a
99 EOF
100
101 hg cat copy2
102
103 echo % rename and modify
104 hg import -mrenamemod - <<EOF
105 diff --git a/copy2 b/rename2
106 similarity index 80%
107 rename from copy2
108 rename to rename2
109 index b53c148..8f81e29 100644
110 --- a/copy2
111 +++ b/rename2
112 @@ -1,5 +1,5 @@
113 a
114 a
115 b
116 -a
117 +c
118 a
119 EOF
120
121 hg locate copy2
122 hg cat rename2
@@ -0,0 +1,34 b''
1 % new file
2 applying patch from stdin
3 % chmod +x
4 applying patch from stdin
5 % copy
6 applying patch from stdin
7 a
8 a
9 % rename
10 applying patch from stdin
11 copyx
12 new
13 rename
14 % delete
15 applying patch from stdin
16 new
17 rename
18 % regular diff
19 applying patch from stdin
20 % copy and modify
21 applying patch from stdin
22 a
23 a
24 b
25 a
26 a
27 % rename and modify
28 applying patch from stdin
29 copy2: No such file or directory
30 a
31 a
32 b
33 c
34 a
@@ -0,0 +1,49 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 cd ..
18
19 echo % directory replaced with file
20
21 hg init c
22 cd c
23 mkdir a
24 echo a > a/a
25 hg commit -Ama
26
27 rm -rf a
28 echo a > a
29
30 echo % should fail - would corrupt dirstate
31 hg add a
32
33 cd ..
34
35 echo % directory replaced with file
36
37 hg init d
38 cd d
39 mkdir b
40 mkdir b/c
41 echo a > b/c/d
42 hg commit -Ama
43 rm -rf b
44 echo a > b
45
46 echo % should fail - would corrupt dirstate
47 hg add b
48
49 exit 0
@@ -0,0 +1,12 b''
1 % file replaced with directory
2 adding a
3 % should fail - would corrupt dirstate
4 abort: file named 'a' already in dirstate
5 % directory replaced with file
6 adding a/a
7 % should fail - would corrupt dirstate
8 abort: directory named 'a' already in dirstate
9 % directory replaced with file
10 adding b/c/d
11 % should fail - would corrupt dirstate
12 abort: directory named 'b' already in dirstate
@@ -0,0 +1,68 b''
1 #!/bin/sh
2
3 hg init a
4
5 cd a
6 echo a > a
7 hg ci -Ama -d '1 0'
8
9 hg cp a b
10 hg ci -mb -d '2 0'
11
12 mkdir dir
13 hg mv b dir
14 hg ci -mc -d '3 0'
15
16 hg mv a b
17 hg ci -md -d '4 0'
18
19 hg mv dir/b e
20 hg ci -me -d '5 0'
21
22 hg log a
23 echo % -f, directory
24 hg log -f dir
25 echo % -f, but no args
26 hg log -f
27 echo % one rename
28 hg log -vf a
29 echo % many renames
30 hg log -vf e
31
32 # log --follow tests
33 hg init ../follow
34 cd ../follow
35 echo base > base
36 hg ci -Ambase -d '1 0'
37
38 echo r1 >> base
39 hg ci -Amr1 -d '1 0'
40 echo r2 >> base
41 hg ci -Amr2 -d '1 0'
42
43 hg up -C 1
44 echo b1 > b1
45 hg ci -Amb1 -d '1 0'
46
47 echo % log -f
48 hg log -f
49
50 hg up -C 0
51 echo b2 > b2
52 hg ci -Amb2 -d '1 0'
53
54 echo % log -f -r 1:tip
55 hg log -f -r 1:tip
56
57 hg up -C 3
58 hg merge tip
59 hg ci -mm12 -d '1 0'
60
61 echo postm >> b1
62 hg ci -Amb1.1 -d'1 0'
63
64 echo % log --follow-first
65 hg log --follow-first
66
67 echo % log -P 2
68 hg log -P 2
@@ -0,0 +1,177 b''
1 adding a
2 changeset: 0:8580ff50825a
3 user: test
4 date: Thu Jan 01 00:00:01 1970 +0000
5 summary: a
6
7 % -f, directory
8 abort: can only follow copies/renames for explicit file names
9 % -f, but no args
10 changeset: 4:8c1c8408f737
11 tag: tip
12 user: test
13 date: Thu Jan 01 00:00:05 1970 +0000
14 summary: e
15
16 changeset: 3:c4ba038c90ce
17 user: test
18 date: Thu Jan 01 00:00:04 1970 +0000
19 summary: d
20
21 changeset: 2:21fba396af4c
22 user: test
23 date: Thu Jan 01 00:00:03 1970 +0000
24 summary: c
25
26 changeset: 1:c0296dabce9b
27 user: test
28 date: Thu Jan 01 00:00:02 1970 +0000
29 summary: b
30
31 changeset: 0:8580ff50825a
32 user: test
33 date: Thu Jan 01 00:00:01 1970 +0000
34 summary: a
35
36 % one rename
37 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
38 user: test
39 date: Thu Jan 01 00:00:01 1970 +0000
40 files: a
41 description:
42 a
43
44
45 % many renames
46 changeset: 4:8c1c8408f7371319750ea2d4fa7969828effbcf4
47 tag: tip
48 user: test
49 date: Thu Jan 01 00:00:05 1970 +0000
50 files: dir/b e
51 description:
52 e
53
54
55 changeset: 2:21fba396af4c801f9717de6c415b6cc9620437e8
56 user: test
57 date: Thu Jan 01 00:00:03 1970 +0000
58 files: b dir/b
59 description:
60 c
61
62
63 changeset: 1:c0296dabce9bf0cd3fdd608de26693c91cd6bbf4
64 user: test
65 date: Thu Jan 01 00:00:02 1970 +0000
66 files: b
67 description:
68 b
69
70
71 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
72 user: test
73 date: Thu Jan 01 00:00:01 1970 +0000
74 files: a
75 description:
76 a
77
78
79 adding base
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 adding b1
82 % log -f
83 changeset: 3:e62f78d544b4
84 tag: tip
85 parent: 1:3d5bf5654eda
86 user: test
87 date: Thu Jan 01 00:00:01 1970 +0000
88 summary: b1
89
90 changeset: 1:3d5bf5654eda
91 user: test
92 date: Thu Jan 01 00:00:01 1970 +0000
93 summary: r1
94
95 changeset: 0:67e992f2c4f3
96 user: test
97 date: Thu Jan 01 00:00:01 1970 +0000
98 summary: base
99
100 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
101 adding b2
102 % log -f -r 1:tip
103 changeset: 1:3d5bf5654eda
104 user: test
105 date: Thu Jan 01 00:00:01 1970 +0000
106 summary: r1
107
108 changeset: 2:60c670bf5b30
109 user: test
110 date: Thu Jan 01 00:00:01 1970 +0000
111 summary: r2
112
113 changeset: 3:e62f78d544b4
114 parent: 1:3d5bf5654eda
115 user: test
116 date: Thu Jan 01 00:00:01 1970 +0000
117 summary: b1
118
119 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 (branch merge, don't forget to commit)
122 % log --follow-first
123 changeset: 6:2404bbcab562
124 tag: tip
125 user: test
126 date: Thu Jan 01 00:00:01 1970 +0000
127 summary: b1.1
128
129 changeset: 5:302e9dd6890d
130 parent: 3:e62f78d544b4
131 parent: 4:ddb82e70d1a1
132 user: test
133 date: Thu Jan 01 00:00:01 1970 +0000
134 summary: m12
135
136 changeset: 3:e62f78d544b4
137 parent: 1:3d5bf5654eda
138 user: test
139 date: Thu Jan 01 00:00:01 1970 +0000
140 summary: b1
141
142 changeset: 1:3d5bf5654eda
143 user: test
144 date: Thu Jan 01 00:00:01 1970 +0000
145 summary: r1
146
147 changeset: 0:67e992f2c4f3
148 user: test
149 date: Thu Jan 01 00:00:01 1970 +0000
150 summary: base
151
152 % log -P 2
153 changeset: 6:2404bbcab562
154 tag: tip
155 user: test
156 date: Thu Jan 01 00:00:01 1970 +0000
157 summary: b1.1
158
159 changeset: 5:302e9dd6890d
160 parent: 3:e62f78d544b4
161 parent: 4:ddb82e70d1a1
162 user: test
163 date: Thu Jan 01 00:00:01 1970 +0000
164 summary: m12
165
166 changeset: 4:ddb82e70d1a1
167 parent: 0:67e992f2c4f3
168 user: test
169 date: Thu Jan 01 00:00:01 1970 +0000
170 summary: b2
171
172 changeset: 3:e62f78d544b4
173 parent: 1:3d5bf5654eda
174 user: test
175 date: Thu Jan 01 00:00:01 1970 +0000
176 summary: b1
177
@@ -0,0 +1,40 b''
1 #!/bin/sh
2
3 hg init
4 echo a > a
5 hg commit -A -ma
6
7 echo a >> a
8 hg commit -mb
9
10 echo a >> a
11 hg commit -mc
12
13 hg up 1
14 echo a >> a
15 hg commit -md
16
17 hg up 1
18 echo a >> a
19 hg commit -me
20
21 hg up 1
22 echo % should fail because not at a head
23 hg merge
24
25 hg up
26 echo % should fail because \> 2 heads
27 hg merge
28
29 echo % should succeed
30 hg merge 2
31 hg commit -mm1
32
33 echo % should succeed - 2 heads
34 hg merge
35 hg commit -mm2
36
37 echo % should fail because 1 head
38 hg merge
39
40 true
@@ -0,0 +1,17 b''
1 adding a
2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 % should fail because not at a head
6 abort: repo has 3 heads - please merge with an explicit rev
7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 % should fail because > 2 heads
9 abort: repo has 3 heads - please merge with an explicit rev
10 % should succeed
11 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 (branch merge, don't forget to commit)
13 % should succeed - 2 heads
14 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 (branch merge, don't forget to commit)
16 % should fail because 1 head
17 abort: there is nothing to merge - use "hg update" instead
@@ -0,0 +1,155 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
7 echo % help
8 hg help mq
9
10 hg init a
11 cd a
12 echo a > a
13 hg ci -Ama
14
15 hg clone . ../k
16
17 mkdir b
18 echo z > b/z
19 hg ci -Ama
20
21 echo % qinit
22
23 hg qinit
24
25 cd ..
26 hg init b
27
28 echo % -R qinit
29
30 hg -R b qinit
31
32 hg init c
33
34 echo % qinit -c
35
36 hg --cwd c qinit -c
37 hg -R c/.hg/patches st
38
39 echo % qnew implies add
40
41 hg -R c qnew test.patch
42 hg -R c/.hg/patches st
43
44 cd a
45
46 echo % qnew -m
47
48 hg qnew -m 'foo bar' test.patch
49 cat .hg/patches/test.patch
50
51 echo % qrefresh
52
53 echo a >> a
54 hg qrefresh
55 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
56 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
57 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
58
59 echo % qpop
60
61 hg qpop
62
63 echo % qpush
64
65 hg qpush
66
67 cd ..
68
69 echo % pop/push outside repo
70
71 hg -R a qpop
72 hg -R a qpush
73
74 cd a
75 hg qnew test2.patch
76
77 echo % qrefresh in subdir
78
79 cd b
80 echo a > a
81 hg add a
82 hg qrefresh
83
84 echo % pop/push -a in subdir
85
86 hg qpop -a
87 hg --traceback qpush -a
88
89 echo % qseries
90 hg qseries
91
92 echo % qapplied
93 hg qapplied
94
95 echo % qtop
96 hg qtop
97
98 echo % qprev
99 hg qprev
100
101 echo % qnext
102 hg qnext
103
104 echo % pop, qnext, qprev, qapplied
105 hg qpop
106 hg qnext
107 hg qprev
108 hg qapplied
109
110 echo % commit should fail
111 hg commit
112
113 echo % push should fail
114 hg push ../../k
115
116 echo % qunapplied
117 hg qunapplied
118
119 echo % push should succeed
120 hg qpop -a
121 hg push ../../k
122
123 echo % strip
124 cd ../../b
125 echo x>x
126 hg ci -Ama
127 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
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
@@ -0,0 +1,101 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
7 hg init
8 hg qinit
9
10 echo x > x
11 hg ci -Ama
12
13 hg qnew a.patch
14 echo a > a
15 hg add a
16 hg qrefresh
17
18 hg qnew b.patch
19 echo b > b
20 hg add b
21 hg qrefresh
22
23 hg qnew c.patch
24 echo c > c
25 hg add c
26 hg qrefresh
27
28 hg qpop -a
29
30 echo % should fail
31 hg qguard +fail
32
33 hg qpush
34 echo % should guard a.patch
35 hg qguard +a
36 echo % should print +a
37 hg qguard
38 hg qpop
39
40 hg qguard a.patch
41 echo % should push b.patch
42 hg qpush
43
44 hg qpop
45 hg qselect a
46 echo % should push a.patch
47 hg qpush
48
49 hg qguard c.patch -a
50 echo % should print -a
51 hg qguard c.patch
52
53 echo % should skip c.patch
54 hg qpush -a
55
56 hg qguard -n c.patch
57 echo % should push c.patch
58 hg qpush -a
59
60 hg qpop -a
61 hg qselect -n
62 echo % should push all
63 hg qpush -a
64
65 hg qpop -a
66 hg qguard a.patch +1
67 hg qguard b.patch +2
68 hg qselect 1
69 echo % should push a.patch, not b.patch
70 hg qpush
71 hg qpush
72 hg qpop -a
73
74 hg qselect 2
75 echo % should push b.patch
76 hg qpush
77 hg qpop -a
78
79 hg qselect 1 2
80 echo % should push a.patch, b.patch
81 hg qpush
82 hg qpush
83 hg qpop -a
84
85 hg qguard a.patch +1 +2 -3
86 hg qselect 1 2 3
87 echo % list patches and guards
88 hg qguard -l
89 echo % list series
90 hg qseries -v
91 echo % list guards
92 hg qselect
93 echo % should push b.patch
94 hg qpush
95
96 hg qpush -a
97 hg qselect -n --reapply
98 echo % guards in series file: +1 +2 -3
99 hg qselect -s
100 echo % should show c.patch
101 hg qapplied
@@ -0,0 +1,84 b''
1 adding x
2 Patch queue now empty
3 % should fail
4 abort: no patches applied
5 applying a.patch
6 Now at: a.patch
7 % should guard a.patch
8 % should print +a
9 a.patch: +a
10 Patch queue now empty
11 a.patch: +a
12 % should push b.patch
13 applying b.patch
14 Now at: b.patch
15 Patch queue now empty
16 number of unguarded, unapplied patches has changed from 2 to 3
17 % should push a.patch
18 applying a.patch
19 Now at: a.patch
20 % should print -a
21 c.patch: -a
22 % should skip c.patch
23 applying b.patch
24 skipping c.patch - guarded by '- a'
25 Now at: b.patch
26 % should push c.patch
27 applying c.patch
28 Now at: c.patch
29 Patch queue now empty
30 guards deactivated
31 number of unguarded, unapplied patches has changed from 3 to 2
32 % should push all
33 applying b.patch
34 applying c.patch
35 Now at: c.patch
36 Patch queue now empty
37 number of unguarded, unapplied patches has changed from 1 to 2
38 % should push a.patch, not b.patch
39 applying a.patch
40 Now at: a.patch
41 applying c.patch
42 Now at: c.patch
43 Patch queue now empty
44 % should push b.patch
45 applying b.patch
46 Now at: b.patch
47 Patch queue now empty
48 number of unguarded, unapplied patches has changed from 2 to 3
49 % should push a.patch, b.patch
50 applying a.patch
51 Now at: a.patch
52 applying b.patch
53 Now at: b.patch
54 Patch queue now empty
55 number of unguarded, unapplied patches has changed from 3 to 2
56 % list patches and guards
57 a.patch: +1 +2 -3
58 b.patch: +2
59 c.patch: unguarded
60 % list series
61 0 G a.patch
62 1 U b.patch
63 2 U c.patch
64 % list guards
65 1
66 2
67 3
68 % should push b.patch
69 applying b.patch
70 Now at: b.patch
71 applying c.patch
72 Now at: c.patch
73 guards deactivated
74 popping guarded patches
75 Patch queue now empty
76 reapplying unguarded patches
77 applying c.patch
78 Now at: c.patch
79 % guards in series file: +1 +2 -3
80 +1
81 +2
82 -3
83 % should show c.patch
84 c.patch
@@ -0,0 +1,28 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
7 echo % init
8 hg init a
9 cd a
10
11 echo % commit
12 echo 'base' > base
13 hg ci -Ambase -d '1 0'
14
15 echo % qnew mqbase
16 hg qnew -mmqbase mqbase
17
18 echo % qrefresh
19 echo 'patched' > base
20 hg qrefresh
21
22 echo % qdiff
23 hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
24 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
25
26 echo % qdiff dirname
27 hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
28 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
@@ -0,0 +1,19 b''
1 % init
2 % commit
3 adding base
4 % qnew mqbase
5 % qrefresh
6 % qdiff
7 diff -r 67e992f2c4f3 base
8 --- a/base
9 +++ b/base
10 @@ -1,1 +1,1 @@ base
11 -base
12 +patched
13 % qdiff dirname
14 diff -r 67e992f2c4f3 base
15 --- a/base
16 +++ b/base
17 @@ -1,1 +1,1 @@ base
18 -base
19 +patched
@@ -0,0 +1,15 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
7 hg init a
8 cd a
9 hg qnew first.patch
10 hg qnew first.patch
11
12 touch ../first.patch
13 hg qimport ../first.patch
14
15 exit 0
@@ -0,0 +1,2 b''
1 abort: patch "first.patch" already exists
2 abort: patch "first.patch" already exists
@@ -0,0 +1,51 b''
1 #!/bin/sh
2
3 # Environement setup for MQ
4 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
5 echo "[extensions]" >> $HGTMP/.hgrc
6 echo "mq=" >> $HGTMP/.hgrc
7
8 #Repo init
9 hg init
10 hg qinit
11
12 hg qnew -m "First commit message" first-patch
13 echo aaaa > file
14 hg add file
15 hg qrefresh
16 echo =======================
17 echo "Should display 'First commit message'"
18 hg log -l1 -v | sed -n '/description/,$p'
19 echo
20
21 # Testing changing message with -m
22 echo bbbb > file
23 hg qrefresh -m "Second commit message"
24 echo =======================
25 echo "Should display 'Second commit message'"
26 hg log -l1 -v | sed -n '/description/,$p'
27 echo
28
29
30 # Testing changing message with -l
31 echo "Third commit message" > logfile
32 echo " This is the 3rd log message" >> logfile
33 echo bbbb > file
34 hg qrefresh -l logfile
35 echo =======================
36 printf "Should display 'Third commit message\\\n This is the 3rd log message'\n"
37 hg log -l1 -v | sed -n '/description/,$p'
38 echo
39
40 # Testing changing message with -l-
41 hg qnew -m "First commit message" second-patch
42 echo aaaa > file2
43 hg add file2
44 echo bbbb > file2
45 (echo "Fifth commit message"
46 echo " This is the 5th log message" >> logfile) |\
47 hg qrefresh -l-
48 echo =======================
49 printf "Should display 'Fifth commit message\\\n This is the 5th log message'\n"
50 hg log -l1 -v | sed -n '/description/,$p'
51 echo
@@ -0,0 +1,29 b''
1 =======================
2 Should display 'First commit message'
3 description:
4 First commit message
5
6
7
8 =======================
9 Should display 'Second commit message'
10 description:
11 Second commit message
12
13
14
15 =======================
16 Should display 'Third commit message\n This is the 3rd log message'
17 description:
18 Third commit message
19 This is the 3rd log message
20
21
22
23 =======================
24 Should display 'Fifth commit message\n This is the 5th log message'
25 description:
26 Fifth commit message
27
28
29
@@ -0,0 +1,16 b''
1 #!/bin/sh
2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
6
7 hg init a
8 cd a
9
10 echo 'base' > base
11 hg ci -Ambase -d '1 0'
12
13 hg qnew -mmqbase mqbase
14
15 hg qsave
16 hg qrestore 2
@@ -0,0 +1,2 b''
1 adding base
2 restoring status: hg patches saved state
@@ -0,0 +1,148 b''
1 % help
2 mq extension - patch management and development
3
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
6 applied patches (subset of known patches).
7
8 Known patches are represented as patch files in the .hg/patches
9 directory. Applied patches are both patch files and changesets.
10
11 Common tasks (use "hg help command" for more details):
12
13 prepare repository to work with patches qinit
14 create new patch qnew
15 import existing patch qimport
16
17 print patch series qseries
18 print applied patches qapplied
19 print name of top applied patch qtop
20
21 add known patch to applied stack qpush
22 remove patch from applied stack qpop
23 refresh contents of top applied patch qrefresh
24
25 list of commands (use "hg help -v mq" to show aliases and global options):
26
27 qapplied print the patches already applied
28 qclone clone main and patch repository at same time
29 qcommit commit changes in the queue repository
30 qdelete remove patches from queue
31 qdiff diff of the current patch
32 qfold fold the named patches into the current patch
33 qguard set or print guards for a patch
34 qheader Print the header of the topmost or specified patch
35 qimport import a patch
36 qinit init a new queue repository
37 qnew create a new patch
38 qnext print the name of the next patch
39 qpop pop the current patch off the stack
40 qprev print the name of the previous patch
41 qpush push the next patch onto the stack
42 qrefresh update the current patch
43 qrename rename a patch
44 qrestore restore the queue state saved by a rev
45 qsave save current queue state
46 qselect set or print guarded patches to push
47 qseries print the entire series file
48 qtop print the name of the current patch
49 qunapplied print the patches not yet applied
50 strip strip a revision and all later revs on the same branch
51 adding a
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 adding b/z
54 % qinit
55 % -R qinit
56 % qinit -c
57 A .hgignore
58 A series
59 % qnew implies add
60 A .hgignore
61 A series
62 A test.patch
63 % qnew -m
64 foo bar
65 % qrefresh
66 foo bar
67
68 diff -r xa
69 --- a/a
70 +++ b/a
71 @@ -1,1 +1,2 @@ a
72 a
73 +a
74 % qpop
75 Patch queue now empty
76 % qpush
77 applying test.patch
78 Now at: test.patch
79 % pop/push outside repo
80 Patch queue now empty
81 applying test.patch
82 Now at: test.patch
83 % qrefresh in subdir
84 % pop/push -a in subdir
85 Patch queue now empty
86 applying test.patch
87 applying test2.patch
88 Now at: test2.patch
89 % qseries
90 test.patch
91 test2.patch
92 % qapplied
93 test.patch
94 test2.patch
95 % qtop
96 test2.patch
97 % qprev
98 test.patch
99 % qnext
100 All patches applied
101 % pop, qnext, qprev, qapplied
102 Now at: test.patch
103 test2.patch
104 Only one patch applied
105 test.patch
106 % commit should fail
107 abort: cannot commit over an applied mq patch
108 % push should fail
109 pushing to ../../k
110 abort: source has mq patches applied
111 % qunapplied
112 test2.patch
113 % push should succeed
114 Patch queue now empty
115 pushing to ../../k
116 searching for changes
117 adding changesets
118 adding manifests
119 adding file changes
120 added 1 changesets with 1 changes to 1 files
121 % strip
122 adding x
123 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 saving bundle to
125 adding changesets
126 adding manifests
127 adding file changes
128 added 1 changesets with 1 changes to 1 files
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
@@ -1,1 +1,2 b''
1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
@@ -11,3 +11,4 b' 979c049974485125e1f9357f6bbe9c1b548a64c3'
11 3a56574f329a368d645853e0f9e09472aee62349 0.8
11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
@@ -4,6 +4,7 b' Goffredo Baroncelli <kreijack at libero.'
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>
@@ -2,7 +2,7 b' include hg'
2 recursive-include mercurial *.py
2 recursive-include mercurial *.py
3 include hgweb.cgi hgwebdir.cgi
3 include hgweb.cgi hgwebdir.cgi
4 include hgeditor rewrite-log
4 include hgeditor rewrite-log
5 include tests/README tests/coverage.py tests/run-tests.py tests/md5sum.py tests/test-*[a-z0-9] tests/*.out
5 include tests/README tests/*.py tests/test-*[a-z0-9] tests/*.out
6 prune tests/*.err
6 prune tests/*.err
7 include *.txt
7 include *.txt
8 include templates/map templates/map-*[a-z0-9]
8 include templates/map templates/map-*[a-z0-9]
@@ -10,8 +10,10 b' include templates/*.tmpl'
10 include templates/static/*
10 include templates/static/*
11 include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9]
11 include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9]
12 recursive-include contrib *
12 recursive-include contrib *
13 recursive-include hgext *
13 include README
14 include README
14 include CONTRIBUTORS
15 include CONTRIBUTORS
15 include COPYING
16 include COPYING
16 include Makefile
17 include Makefile
17 include MANIFEST.in
18 include MANIFEST.in
19 prune *.elc *.orig *.rej *~ *.o *.so *.pyc *.swp *.prof
@@ -288,7 +288,7 b' complete -o bashdefault -o default -F _h'
288
288
289 _hg_cmd_qdelete()
289 _hg_cmd_qdelete()
290 {
290 {
291 _hg_ext_mq_patchlist qseries
291 _hg_ext_mq_patchlist qunapplied
292 }
292 }
293
293
294 _hg_cmd_qsave()
294 _hg_cmd_qsave()
@@ -313,6 +313,11 b' complete -o bashdefault -o default -F _h'
313 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
313 COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
314 }
314 }
315
315
316 _hg_cmd_export()
317 {
318 _hg_ext_mq_patchlist qapplied
319 }
320
316
321
317 # hbisect
322 # hbisect
318 _hg_cmd_bisect()
323 _hg_cmd_bisect()
@@ -28,7 +28,8 b' class convert_git:'
28 self.path = path
28 self.path = path
29
29
30 def getheads(self):
30 def getheads(self):
31 return [file(self.path + "/HEAD").read()[:-1]]
31 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
32 return [fh.read()[:-1]]
32
33
33 def catfile(self, rev, type):
34 def catfile(self, rev, type):
34 if rev == "0" * 40: raise IOError()
35 if rev == "0" * 40: raise IOError()
@@ -92,7 +92,7 b' def darcs_tip(darcs_repo):'
92
92
93 def darcs_pull(hg_repo, darcs_repo, chash):
93 def darcs_pull(hg_repo, darcs_repo, chash):
94 old_tip = darcs_tip(darcs_repo)
94 old_tip = darcs_tip(darcs_repo)
95 res = cmd("darcs pull '%s' --all --match='hash %s'" % (darcs_repo, chash), hg_repo)
95 res = cmd("darcs pull \"%s\" --all --match=\"hash %s\"" % (darcs_repo, chash), hg_repo)
96 print res
96 print res
97 new_tip = darcs_tip(darcs_repo)
97 new_tip = darcs_tip(darcs_repo)
98 if not new_tip != old_tip + 1:
98 if not new_tip != old_tip + 1:
@@ -110,7 +110,8 b' def hg_commit( hg_repo, text, author, da'
110 old_tip = hg_tip(hg_repo)
110 old_tip = hg_tip(hg_repo)
111 cmd("hg add -X _darcs", hg_repo)
111 cmd("hg add -X _darcs", hg_repo)
112 cmd("hg remove -X _darcs --after", hg_repo)
112 cmd("hg remove -X _darcs --after", hg_repo)
113 res = cmd("hg commit -l %s -u '%s' -d '%s 0'" % (tmpfile, author, date), hg_repo)
113 res = cmd("hg commit -l %s -u \"%s\" -d \"%s 0\"" % (tmpfile, author, date), hg_repo)
114 os.close(fd)
114 os.unlink(tmpfile)
115 os.unlink(tmpfile)
115 new_tip = hg_tip(hg_repo)
116 new_tip = hg_tip(hg_repo)
116 if not new_tip == old_tip + 1:
117 if not new_tip == old_tip + 1:
@@ -156,7 +157,7 b' if __name__ == "__main__":'
156 print "Given HG repository must not exist when no SKIP is specified."
157 print "Given HG repository must not exist when no SKIP is specified."
157 sys.exit(-1)
158 sys.exit(-1)
158 if skip == None:
159 if skip == None:
159 cmd("hg init '%s'" % (hg_repo))
160 cmd("hg init \"%s\"" % (hg_repo))
160 cmd("darcs initialize", hg_repo)
161 cmd("darcs initialize", hg_repo)
161 # Get the changes from the Darcs repository
162 # Get the changes from the Darcs repository
162 change_number = 0
163 change_number = 0
@@ -5,13 +5,73 b''
5 <meta http-equiv="Content-Style-Type" content="text/css">
5 <meta http-equiv="Content-Style-Type" content="text/css">
6 <title></title>
6 <title></title>
7 <style type="text/css">
7 <style type="text/css">
8 p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}
8 p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica}
9 p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
9 p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
10 </style>
10 </style>
11 </head>
11 </head>
12 <body>
12 <body>
13 <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
13 <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
14 <p class="p2"><br></p>
14 <p class="p2"><br></p>
15 <p class="p1">It is based on Mercurial 0.9.</p>
15 <p class="p1">It is based on Mercurial 0.9.1</p>
16 <br>
17 <pre>
18 Release Notes
19 -------------
20
21 2006-07-24 v0.9.1
22
23 Major changes between Mercurial 0.9 and 0.9.1:
24
25 New features:
26 - You can now configure your 'hgweb' server to let remote users
27 'push' changes over http.
28 - You can now 'import' a patch in a mail message by saving the mail
29 message, and importing it. This works for patches sent either
30 inline or as attachments.
31 - The 'diff' command now accepts '-rA:B' syntax as a synonym for
32 '-r A -r B', and adds '-b' and '-B' options.
33
34 New contributions and extensions:
35 - The 'acl' extension lets you lock down parts of a repository
36 against incoming changes
37 - The 'extdiff' extension lets you run your favourite graphical
38 change viewer
39 - Comprehensive integration with the 'vim' editor
40 - A restricted shell for 'ssh'-hosted repositories
41 - An importer for 'darcs' repositories
42
43 New hooks added:
44 - 'preupdate' is run before an update or merge in the working
45 directory.
46 - 'update' is run after an update or merge in the working
47 directory.
48
49 Behaviour changes:
50 - NOTE: Mercurial as installed by the Windows binary
51 installer no longer performs automatic line-ending conversion for
52 Unix/Linux compatibility. To re-enable this feature, edit your
53 'mercurial.ini' file after you upgrade.
54 - The Windows binary installer now automatically adds 'hg' to your
55 '%PATH%'.
56 - The 'backout' command now runs an editor by default, to let you
57 modify the commit message for a backed-out changeset.
58 - An earlier problem with parsing of tags has been fixed.
59 This makes tag parsing slower but more reliable.
60
61 Memory usage and performance improvements:
62 - The 'remove' command has been rewritten to be hundreds of times
63 faster in large repositories.
64 - It is now possible to 'clone' a repository very quickly over a
65 LAN, if the server is configured to allow it. See the new 'server'
66 section in the 'hgrc' documentation.
67
68 Other changes of note:
69 - Mercurial will now print help for an extension if you type 'hg
70 help EXT_NAME'.
71 - The usual array of bug fixes and documentation improvements.
72 - The integrated web server is now more WSGI-compliant.
73 - Work has begun to solidify Mercurial's API for use by third-party
74 packages.
75 </pre>
16 </body>
76 </body>
17 </html>
77 </html>
@@ -380,7 +380,9 b" Handle frickin' frackin' gratuitous even"
380 (save-excursion
380 (save-excursion
381 (while hg-prev-buffer
381 (while hg-prev-buffer
382 (set-buffer hg-prev-buffer))
382 (set-buffer hg-prev-buffer))
383 (let ((path (or default (buffer-file-name) default-directory)))
383 (let ((path (or default
384 (buffer-file-name)
385 (expand-file-name default-directory))))
384 (if (or (not path) current-prefix-arg)
386 (if (or (not path) current-prefix-arg)
385 (expand-file-name
387 (expand-file-name
386 (eval (list* 'read-file-name
388 (eval (list* 'read-file-name
@@ -716,7 +718,11 b' code by typing `M-x find-library mercuri'
716 (goto-char pos)
718 (goto-char pos)
717 (end-of-line 1)
719 (end-of-line 1)
718 (delete-region pos (point)))
720 (delete-region pos (point)))
719 (cd (hg-root))))
721 (let ((hg-root-dir (hg-root)))
722 (if (not hg-root-dir)
723 (error "error: %s: directory is not part of a Mercurial repository."
724 default-directory)
725 (cd hg-root-dir)))))
720
726
721 (defun hg-add (path)
727 (defun hg-add (path)
722 "Add PATH to the Mercurial repository on the next commit.
728 "Add PATH to the Mercurial repository on the next commit.
@@ -972,7 +978,8 b' With a prefix argument, prompt for the p'
972 (cd (hg-root path)))
978 (cd (hg-root path)))
973 (when update
979 (when update
974 (with-current-buffer buf
980 (with-current-buffer buf
975 (set (make-local-variable 'backup-inhibited) nil)
981 (when (local-variable-p 'backup-inhibited)
982 (kill-local-variable 'backup-inhibited))
976 (hg-mode-line)))))
983 (hg-mode-line)))))
977
984
978 (defun hg-incoming (&optional repo)
985 (defun hg-incoming (&optional repo)
This diff has been collapsed as it changes many lines, (686 lines changed) Show them Hide them
@@ -3,7 +3,7 b''
3 " Vim plugin to assist in working with HG-controlled files.
3 " Vim plugin to assist in working with HG-controlled files.
4 "
4 "
5 " Last Change: 2006/02/22
5 " Last Change: 2006/02/22
6 " Version: 1.76
6 " Version: 1.77
7 " Maintainer: Mathieu Clabaut <mathieu.clabaut@gmail.com>
7 " Maintainer: Mathieu Clabaut <mathieu.clabaut@gmail.com>
8 " License: This file is placed in the public domain.
8 " License: This file is placed in the public domain.
9 " Credits:
9 " Credits:
@@ -13,7 +13,7 b''
13
13
14 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
14 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
15 "
15 "
16 " Section: Documentation
16 " Section: Documentation
17 "----------------------------
17 "----------------------------
18 "
18 "
19 " Documentation should be available by ":help hgcommand" command, once the
19 " Documentation should be available by ":help hgcommand" command, once the
@@ -21,7 +21,7 b''
21 "
21 "
22 " You still can read the documentation at the end of this file. Locate it by
22 " You still can read the documentation at the end of this file. Locate it by
23 " searching the "hgcommand-contents" string (and set ft=help to have
23 " searching the "hgcommand-contents" string (and set ft=help to have
24 " appropriate syntaxic coloration).
24 " appropriate syntaxic coloration).
25
25
26 " Section: Plugin header {{{1
26 " Section: Plugin header {{{1
27
27
@@ -34,11 +34,33 b' if exists("loaded_hgcommand")'
34 endif
34 endif
35 let loaded_hgcommand = 1
35 let loaded_hgcommand = 1
36
36
37 " store 'compatible' settings
38 let s:save_cpo = &cpo
39 set cpo&vim
40
41 " run checks
42 let s:script_name = expand("<sfile>:t:r")
43
44 function! s:HGCleanupOnFailure(err)
45 echohl WarningMsg
46 echomsg s:script_name . ":" a:err "Plugin not loaded"
47 echohl None
48 let loaded_hgcommand = "no"
49 unlet s:save_cpo s:script_name
50 endfunction
51
37 if v:version < 602
52 if v:version < 602
38 echohl WarningMsg|echomsg "HGCommand 1.69 or later requires VIM 6.2 or later"|echohl None
53 call <SID>HGCleanupOnFailure("VIM 6.2 or later required.")
39 finish
54 finish
40 endif
55 endif
41
56
57 if !exists("*system")
58 call <SID>HGCleanupOnFailure("builtin system() function required.")
59 finish
60 endif
61
62 let s:script_version = "v0.2"
63
42 " Section: Event group setup {{{1
64 " Section: Event group setup {{{1
43
65
44 augroup HGCommand
66 augroup HGCommand
@@ -63,7 +85,7 b' unlet! s:vimDiffScratchList'
63 function! s:HGResolveLink(fileName)
85 function! s:HGResolveLink(fileName)
64 let resolved = resolve(a:fileName)
86 let resolved = resolve(a:fileName)
65 if resolved != a:fileName
87 if resolved != a:fileName
66 let resolved = s:HGResolveLink(resolved)
88 let resolved = <SID>HGResolveLink(resolved)
67 endif
89 endif
68 return resolved
90 return resolved
69 endfunction
91 endfunction
@@ -74,7 +96,7 b' endfunction'
74
96
75 function! s:HGChangeToCurrentFileDir(fileName)
97 function! s:HGChangeToCurrentFileDir(fileName)
76 let oldCwd=getcwd()
98 let oldCwd=getcwd()
77 let fileName=s:HGResolveLink(a:fileName)
99 let fileName=<SID>HGResolveLink(a:fileName)
78 let newCwd=fnamemodify(fileName, ':h')
100 let newCwd=fnamemodify(fileName, ':h')
79 if strlen(newCwd) > 0
101 if strlen(newCwd) > 0
80 execute 'cd' escape(newCwd, ' ')
102 execute 'cd' escape(newCwd, ' ')
@@ -82,7 +104,7 b' function! s:HGChangeToCurrentFileDir(fil'
82 return oldCwd
104 return oldCwd
83 endfunction
105 endfunction
84
106
85 " Function: s:HGGetOption(name, default) {{{2
107 " Function: <SID>HGGetOption(name, default) {{{2
86 " Grab a user-specified option to override the default provided. Options are
108 " Grab a user-specified option to override the default provided. Options are
87 " searched in the window, buffer, then global spaces.
109 " searched in the window, buffer, then global spaces.
88
110
@@ -110,9 +132,9 b' function! s:HGEditFile(name, origBuffNR)'
110 "Name parameter will be pasted into expression.
132 "Name parameter will be pasted into expression.
111 let name = escape(a:name, ' *?\')
133 let name = escape(a:name, ' *?\')
112
134
113 let editCommand = s:HGGetOption('HGCommandEdit', 'edit')
135 let editCommand = <SID>HGGetOption('HGCommandEdit', 'edit')
114 if editCommand != 'edit'
136 if editCommand != 'edit'
115 if s:HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal'
137 if <SID>HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal'
116 if name == ""
138 if name == ""
117 let editCommand = 'rightbelow new'
139 let editCommand = 'rightbelow new'
118 else
140 else
@@ -154,8 +176,8 b' function! s:HGCreateCommandBuffer(cmd, c'
154
176
155 let resultBufferName=''
177 let resultBufferName=''
156
178
157 if s:HGGetOption("HGCommandNameResultBuffers", 0)
179 if <SID>HGGetOption("HGCommandNameResultBuffers", 0)
158 let nameMarker = s:HGGetOption("HGCommandNameMarker", '_')
180 let nameMarker = <SID>HGGetOption("HGCommandNameMarker", '_')
159 if strlen(a:statusText) > 0
181 if strlen(a:statusText) > 0
160 let bufName=a:cmdName . ' -- ' . a:statusText
182 let bufName=a:cmdName . ' -- ' . a:statusText
161 else
183 else
@@ -170,7 +192,7 b' function! s:HGCreateCommandBuffer(cmd, c'
170 endwhile
192 endwhile
171 endif
193 endif
172
194
173 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd
195 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd
174 "echomsg "DBG :".hgCommand
196 "echomsg "DBG :".hgCommand
175 let hgOut = system(hgCommand)
197 let hgOut = system(hgCommand)
176 " HACK: diff command does not return proper error codes
198 " HACK: diff command does not return proper error codes
@@ -192,7 +214,7 b' function! s:HGCreateCommandBuffer(cmd, c'
192 return -1
214 return -1
193 endif
215 endif
194
216
195 if s:HGEditFile(resultBufferName, a:origBuffNR) == -1
217 if <SID>HGEditFile(resultBufferName, a:origBuffNR) == -1
196 return -1
218 return -1
197 endif
219 endif
198
220
@@ -200,7 +222,7 b' function! s:HGCreateCommandBuffer(cmd, c'
200 set noswapfile
222 set noswapfile
201 set filetype=
223 set filetype=
202
224
203 if s:HGGetOption("HGCommandDeleteOnHide", 0)
225 if <SID>HGGetOption("HGCommandDeleteOnHide", 0)
204 set bufhidden=delete
226 set bufhidden=delete
205 endif
227 endif
206
228
@@ -213,8 +235,8 b' function! s:HGCreateCommandBuffer(cmd, c'
213 " This could be fixed by explicitly detecting whether the last line is
235 " This could be fixed by explicitly detecting whether the last line is
214 " within a fold, but I prefer to simply unfold the result buffer altogether.
236 " within a fold, but I prefer to simply unfold the result buffer altogether.
215
237
216 if has('folding')
238 if has("folding")
217 normal zR
239 setlocal nofoldenable
218 endif
240 endif
219
241
220 $d
242 $d
@@ -243,7 +265,7 b' function! s:HGBufferCheck(hgBuffer)'
243 return origBuffer
265 return origBuffer
244 else
266 else
245 " Original buffer no longer exists.
267 " Original buffer no longer exists.
246 return -1
268 return -1
247 endif
269 endif
248 else
270 else
249 " No original buffer
271 " No original buffer
@@ -256,7 +278,7 b' endfunction'
256 " for the current buffer.
278 " for the current buffer.
257
279
258 function! s:HGCurrentBufferCheck()
280 function! s:HGCurrentBufferCheck()
259 return s:HGBufferCheck(bufnr("%"))
281 return <SID>HGBufferCheck(bufnr("%"))
260 endfunction
282 endfunction
261
283
262 " Function: s:HGToggleDeleteOnHide() {{{2
284 " Function: s:HGToggleDeleteOnHide() {{{2
@@ -275,8 +297,8 b' endfunction'
275 " Returns: name of the new command buffer containing the command results
297 " Returns: name of the new command buffer containing the command results
276
298
277 function! s:HGDoCommand(cmd, cmdName, statusText)
299 function! s:HGDoCommand(cmd, cmdName, statusText)
278 let hgBufferCheck=s:HGCurrentBufferCheck()
300 let hgBufferCheck=<SID>HGCurrentBufferCheck()
279 if hgBufferCheck == -1
301 if hgBufferCheck == -1
280 echo "Original buffer no longer exists, aborting."
302 echo "Original buffer no longer exists, aborting."
281 return -1
303 return -1
282 endif
304 endif
@@ -285,8 +307,8 b' function! s:HGDoCommand(cmd, cmdName, st'
285 if isdirectory(fileName)
307 if isdirectory(fileName)
286 let fileName=fileName . "/" . getline(".")
308 let fileName=fileName . "/" . getline(".")
287 endif
309 endif
288 let realFileName = fnamemodify(s:HGResolveLink(fileName), ':t')
310 let realFileName = fnamemodify(<SID>HGResolveLink(fileName), ':t')
289 let oldCwd=s:HGChangeToCurrentFileDir(fileName)
311 let oldCwd=<SID>HGChangeToCurrentFileDir(fileName)
290 try
312 try
291 " TODO
313 " TODO
292 "if !filereadable('HG/Root')
314 "if !filereadable('HG/Root')
@@ -294,7 +316,7 b' function! s:HGDoCommand(cmd, cmdName, st'
294 "endif
316 "endif
295 let fullCmd = a:cmd . ' "' . realFileName . '"'
317 let fullCmd = a:cmd . ' "' . realFileName . '"'
296 "echomsg "DEBUG".fullCmd
318 "echomsg "DEBUG".fullCmd
297 let resultBuffer=s:HGCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, hgBufferCheck)
319 let resultBuffer=<SID>HGCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, hgBufferCheck)
298 return resultBuffer
320 return resultBuffer
299 catch
321 catch
300 echoerr v:exception
322 echoerr v:exception
@@ -314,17 +336,17 b' endfunction'
314 " Returns: string to be exec'd that sets the multiple return values.
336 " Returns: string to be exec'd that sets the multiple return values.
315
337
316 function! s:HGGetStatusVars(revisionVar, branchVar, repositoryVar)
338 function! s:HGGetStatusVars(revisionVar, branchVar, repositoryVar)
317 let hgBufferCheck=s:HGCurrentBufferCheck()
339 let hgBufferCheck=<SID>HGCurrentBufferCheck()
318 "echomsg "DBG : in HGGetStatusVars"
340 "echomsg "DBG : in HGGetStatusVars"
319 if hgBufferCheck == -1
341 if hgBufferCheck == -1
320 return ""
342 return ""
321 endif
343 endif
322 let fileName=bufname(hgBufferCheck)
344 let fileName=bufname(hgBufferCheck)
323 let fileNameWithoutLink=s:HGResolveLink(fileName)
345 let fileNameWithoutLink=<SID>HGResolveLink(fileName)
324 let realFileName = fnamemodify(fileNameWithoutLink, ':t')
346 let realFileName = fnamemodify(fileNameWithoutLink, ':t')
325 let oldCwd=s:HGChangeToCurrentFileDir(realFileName)
347 let oldCwd=<SID>HGChangeToCurrentFileDir(realFileName)
326 try
348 try
327 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " root "
349 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " root "
328 let roottext=system(hgCommand)
350 let roottext=system(hgCommand)
329 " Suppress ending null char ! Does it work in window ?
351 " Suppress ending null char ! Does it work in window ?
330 let roottext=substitute(roottext,'^.*/\([^/\n\r]*\)\n\_.*$','\1','')
352 let roottext=substitute(roottext,'^.*/\([^/\n\r]*\)\n\_.*$','\1','')
@@ -335,31 +357,31 b' function! s:HGGetStatusVars(revisionVar,'
335 if a:repositoryVar != ""
357 if a:repositoryVar != ""
336 let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . roottext . "'"
358 let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . roottext . "'"
337 endif
359 endif
338 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName
360 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName
339 let statustext=system(hgCommand)
361 let statustext=system(hgCommand)
340 if(v:shell_error)
362 if(v:shell_error)
341 return ""
363 return ""
342 endif
364 endif
343 if match(statustext, '^[?I]') >= 0
365 if match(statustext, '^[?I]') >= 0
344 let revision="NEW"
366 let revision="NEW"
345 elseif match(statustext, '^[R]') >= 0
367 elseif match(statustext, '^[R]') >= 0
346 let revision="REMOVED"
368 let revision="REMOVED"
347 elseif match(statustext, '^[D]') >= 0
369 elseif match(statustext, '^[D]') >= 0
348 let revision="DELETED"
370 let revision="DELETED"
349 elseif match(statustext, '^[A]') >= 0
371 elseif match(statustext, '^[A]') >= 0
350 let revision="ADDED"
372 let revision="ADDED"
351 else
373 else
352 " The file is tracked, we can try to get is revision number
374 " The file is tracked, we can try to get is revision number
353 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " parents -b "
375 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " parents -b "
354 let statustext=system(hgCommand)
376 let statustext=system(hgCommand)
355 if(v:shell_error)
377 if(v:shell_error)
356 return ""
378 return ""
357 endif
379 endif
358 let revision=substitute(statustext, '^changeset:\s*\(\d\+\):.*\_$\_.*$', '\1', "")
380 let revision=substitute(statustext, '^changeset:\s*\(\d\+\):.*\_$\_.*$', '\1', "")
359
381
360 if a:branchVar != "" && match(statustext, '^\_.*\_^branch:') >= 0
382 if a:branchVar != "" && match(statustext, '^\_.*\_^branch:') >= 0
361 let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "")
383 let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "")
362 let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
384 let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
363 endif
385 endif
364 endif
386 endif
365 if (exists('revision'))
387 if (exists('revision'))
@@ -381,7 +403,7 b' function! s:HGSetupBuffer(...)'
381 return
403 return
382 endif
404 endif
383
405
384 if !s:HGGetOption("HGCommandEnableBufferSetup", 0)
406 if !<SID>HGGetOption("HGCommandEnableBufferSetup", 0)
385 \ || @% == ""
407 \ || @% == ""
386 \ || s:HGCommandEditFileRunning > 0
408 \ || s:HGCommandEditFileRunning > 0
387 \ || exists("b:HGOrigBuffNR")
409 \ || exists("b:HGOrigBuffNR")
@@ -399,7 +421,7 b' function! s:HGSetupBuffer(...)'
399 let branch=""
421 let branch=""
400 let repository=""
422 let repository=""
401
423
402 exec s:HGGetStatusVars('revision', 'branch', 'repository')
424 exec <SID>HGGetStatusVars('revision', 'branch', 'repository')
403 "echomsg "DBG ".revision."#".branch."#".repository
425 "echomsg "DBG ".revision."#".branch."#".repository
404 if revision != ""
426 if revision != ""
405 let b:HGRevision=revision
427 let b:HGRevision=revision
@@ -427,7 +449,7 b' endfunction'
427 function! s:HGMarkOrigBufferForSetup(hgBuffer)
449 function! s:HGMarkOrigBufferForSetup(hgBuffer)
428 checktime
450 checktime
429 if a:hgBuffer != -1
451 if a:hgBuffer != -1
430 let origBuffer = s:HGBufferCheck(a:hgBuffer)
452 let origBuffer = <SID>HGBufferCheck(a:hgBuffer)
431 "This should never not work, but I'm paranoid
453 "This should never not work, but I'm paranoid
432 if origBuffer != a:hgBuffer
454 if origBuffer != a:hgBuffer
433 call setbufvar(origBuffer, "HGBufferSetup", 0)
455 call setbufvar(origBuffer, "HGBufferSetup", 0)
@@ -436,7 +458,7 b' function! s:HGMarkOrigBufferForSetup(hgB'
436 "We are presumably in the original buffer
458 "We are presumably in the original buffer
437 let b:HGBufferSetup = 0
459 let b:HGBufferSetup = 0
438 "We do the setup now as now event will be triggered allowing it later.
460 "We do the setup now as now event will be triggered allowing it later.
439 call s:HGSetupBuffer()
461 call <SID>HGSetupBuffer()
440 endif
462 endif
441 return a:hgBuffer
463 return a:hgBuffer
442 endfunction
464 endfunction
@@ -478,111 +500,93 b' endfunction'
478 " 1 if new document installed, 0 otherwise.
500 " 1 if new document installed, 0 otherwise.
479 " Note: Cleaned and generalized by guo-peng Wen
501 " Note: Cleaned and generalized by guo-peng Wen
480 "'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
502 "'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
481
503 " Helper function to make mkdir as portable as possible
482 function! s:HGInstallDocumentation(full_name, revision)
504 function! s:HGFlexiMkdir(dir)
483 " Name of the document path based on the system we use:
505 if exists("*mkdir") " we can use Vim's own mkdir()
484 if (has("unix"))
506 call mkdir(a:dir)
485 " On UNIX like system, using forward slash:
507 elseif !exists("+shellslash")
486 let l:slash_char = '/'
508 call system("mkdir -p '".a:dir."'")
487 let l:mkdir_cmd = ':silent !mkdir -p '
509 else " M$
488 else
510 let l:ssl = &shellslash
489 " On M$ system, use backslash. Also mkdir syntax is different.
511 try
490 " This should only work on W2K and up.
512 set shellslash
491 let l:slash_char = '\'
513 " no single quotes?
492 let l:mkdir_cmd = ':silent !mkdir '
514 call system('mkdir "'.a:dir.'"')
493 endif
515 finally
494
516 let &shellslash = l:ssl
495 let l:doc_path = l:slash_char . 'doc'
517 endtry
496 let l:doc_home = l:slash_char . '.vim' . l:slash_char . 'doc'
518 endif
519 endfunction
497
520
498 " Figure out document path based on full name of this script:
521 function! s:HGInstallDocumentation(full_name)
499 let l:vim_plugin_path = fnamemodify(a:full_name, ':h')
522 " Figure out document path based on full name of this script:
500 let l:vim_doc_path = fnamemodify(a:full_name, ':h:h') . l:doc_path
523 let l:vim_doc_path = fnamemodify(a:full_name, ":h:h") . "/doc"
501 if (!(filewritable(l:vim_doc_path) == 2))
524 if filewritable(l:vim_doc_path) != 2
502 echomsg "Doc path: " . l:vim_doc_path
525 echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path
503 execute l:mkdir_cmd . '"' . l:vim_doc_path . '"'
526 silent! call <SID>HGFlexiMkdir(l:vim_doc_path)
504 if (!(filewritable(l:vim_doc_path) == 2))
527 if filewritable(l:vim_doc_path) != 2
505 " Try a default configuration in user home:
528 " Try first item in 'runtimepath':
506 let l:vim_doc_path = expand("~") . l:doc_home
529 let l:vim_doc_path =
507 if (!(filewritable(l:vim_doc_path) == 2))
530 \ substitute(&runtimepath, '^\([^,]*\).*', '\1/doc', 'e')
508 execute l:mkdir_cmd . '"' . l:vim_doc_path . '"'
531 if filewritable(l:vim_doc_path) != 2
509 if (!(filewritable(l:vim_doc_path) == 2))
532 echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path
510 " Put a warning:
533 silent! call <SID>HGFlexiMkdir(l:vim_doc_path)
511 echomsg "Unable to open documentation directory"
534 if filewritable(l:vim_doc_path) != 2
512 echomsg " type :help add-local-help for more informations."
535 " Put a warning:
513 return 0
536 echomsg "Unable to open documentation directory"
514 endif
537 echomsg " type `:help add-local-help' for more information."
515 endif
538 return 0
516 endif
539 endif
540 endif
517 endif
541 endif
518
542 endif
519 " Exit if we have problem to access the document directory:
520 if (!isdirectory(l:vim_plugin_path)
521 \ || !isdirectory(l:vim_doc_path)
522 \ || filewritable(l:vim_doc_path) != 2)
523 return 0
524 endif
525
526 " Full name of script and documentation file:
527 let l:script_name = fnamemodify(a:full_name, ':t')
528 let l:doc_name = fnamemodify(a:full_name, ':t:r') . '.txt'
529 let l:plugin_file = l:vim_plugin_path . l:slash_char . l:script_name
530 let l:doc_file = l:vim_doc_path . l:slash_char . l:doc_name
531
543
532 " Bail out if document file is still up to date:
544 " Full name of documentation file:
533 if (filereadable(l:doc_file) &&
545 let l:doc_file =
534 \ getftime(l:plugin_file) < getftime(l:doc_file))
546 \ l:vim_doc_path . "/" . s:script_name . ".txt"
535 return 0
547 " Bail out if document file is still up to date:
536 endif
548 if filereadable(l:doc_file) &&
549 \ getftime(a:full_name) < getftime(l:doc_file)
550 return 0
551 endif
537
552
538 " Prepare window position restoring command:
553 " temporary global settings
539 if (strlen(@%))
554 let l:lz = &lazyredraw
540 let l:go_back = 'b ' . bufnr("%")
555 let l:hls = &hlsearch
541 else
556 set lazyredraw nohlsearch
542 let l:go_back = 'enew!'
557 " Create a new buffer & read in the plugin file (me):
543 endif
558 1 new
544
559 setlocal noswapfile modifiable nomodeline
545 " Create a new buffer & read in the plugin file (me):
560 if has("folding")
546 setl nomodeline
561 setlocal nofoldenable
547 exe 'enew!'
562 endif
548 exe 'r ' . l:plugin_file
563 silent execute "read" escape(a:full_name, " ")
549
564 let l:doc_buf = bufnr("%")
550 setl modeline
551 let l:buf = bufnr("%")
552 setl noswapfile modifiable
553
554 norm zR
555 norm gg
556
565
557 " Delete from first line to a line starts with
566 1
558 " === START_DOC
567 " Delete from first line to a line starts with
559 1,/^=\{3,}\s\+START_DOC\C/ d
568 " === START_DOC
560
569 silent 1,/^=\{3,}\s\+START_DOC\C/ d
561 " Delete from a line starts with
570 " Delete from a line starts with
562 " === END_DOC
571 " === END_DOC
563 " to the end of the documents:
572 " to the end of the documents:
564 /^=\{3,}\s\+END_DOC\C/,$ d
573 silent /^=\{3,}\s\+END_DOC\C/,$ d
565
566 " Remove fold marks:
567 %s/{\{3}[1-9]/ /
568
574
569 " Add modeline for help doc: the modeline string is mangled intentionally
575 " Add modeline for help doc: the modeline string is mangled intentionally
570 " to avoid it be recognized by VIM:
576 " to avoid it be recognized by VIM:
571 call append(line('$'), '')
577 call append(line("$"), "")
572 call append(line('$'), ' v' . 'im:tw=78:ts=8:ft=help:norl:')
578 call append(line("$"), " v" . "im:tw=78:ts=8:ft=help:norl:")
573
574 " Replace revision:
575 exe "normal :1s/#version#/ v" . a:revision . "/\<CR>"
576
579
577 " Save the help document:
580 " Replace revision:
578 exe 'w! ' . l:doc_file
581 silent execute "normal :1s/#version#/" . s:script_version . "/\<CR>"
579 exe l:go_back
582 " Save the help document and wipe out buffer:
580 exe 'bw ' . l:buf
583 silent execute "wq!" escape(l:doc_file, " ") "| bw" l:doc_buf
584 " Build help tags:
585 silent execute "helptags" l:vim_doc_path
581
586
582 " Build help tags:
587 let &hlsearch = l:hls
583 exe 'helptags ' . l:vim_doc_path
588 let &lazyredraw = l:lz
584
589 return 1
585 return 1
586 endfunction
590 endfunction
587
591
588 " Section: Public functions {{{1
592 " Section: Public functions {{{1
@@ -593,7 +597,7 b' endfunction'
593
597
594 function! HGGetRevision()
598 function! HGGetRevision()
595 let revision=""
599 let revision=""
596 exec s:HGGetStatusVars('revision', '', '')
600 exec <SID>HGGetStatusVars('revision', '', '')
597 return revision
601 return revision
598 endfunction
602 endfunction
599
603
@@ -612,16 +616,16 b' function! HGEnableBufferSetup()'
612 let g:HGCommandEnableBufferSetup=1
616 let g:HGCommandEnableBufferSetup=1
613 augroup HGCommandPlugin
617 augroup HGCommandPlugin
614 au!
618 au!
615 au BufEnter * call s:HGSetupBuffer()
619 au BufEnter * call <SID>HGSetupBuffer()
616 au BufWritePost * call s:HGSetupBuffer()
620 au BufWritePost * call <SID>HGSetupBuffer()
617 " Force resetting up buffer on external file change (HG update)
621 " Force resetting up buffer on external file change (HG update)
618 au FileChangedShell * call s:HGSetupBuffer(1)
622 au FileChangedShell * call <SID>HGSetupBuffer(1)
619 augroup END
623 augroup END
620
624
621 " Only auto-load if the plugin is fully loaded. This gives other plugins a
625 " Only auto-load if the plugin is fully loaded. This gives other plugins a
622 " chance to run.
626 " chance to run.
623 if g:loaded_hgcommand == 2
627 if g:loaded_hgcommand == 2
624 call s:HGSetupBuffer()
628 call <SID>HGSetupBuffer()
625 endif
629 endif
626 endfunction
630 endfunction
627
631
@@ -662,7 +666,7 b' endfunction'
662
666
663 " Function: s:HGAdd() {{{2
667 " Function: s:HGAdd() {{{2
664 function! s:HGAdd()
668 function! s:HGAdd()
665 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('add', 'hgadd', ''))
669 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('add', 'hgadd', ''))
666 endfunction
670 endfunction
667
671
668 " Function: s:HGAnnotate(...) {{{2
672 " Function: s:HGAnnotate(...) {{{2
@@ -672,7 +676,7 b' function! s:HGAnnotate(...)'
672 " This is a HGAnnotate buffer. Perform annotation of the version
676 " This is a HGAnnotate buffer. Perform annotation of the version
673 " indicated by the current line.
677 " indicated by the current line.
674 let revision = substitute(getline("."),'\(^[0-9]*\):.*','\1','')
678 let revision = substitute(getline("."),'\(^[0-9]*\):.*','\1','')
675 if s:HGGetOption('HGCommandAnnotateParent', 0) != 0 && revision > 0
679 if <SID>HGGetOption('HGCommandAnnotateParent', 0) != 0 && revision > 0
676 let revision = revision - 1
680 let revision = revision - 1
677 endif
681 endif
678 else
682 else
@@ -691,7 +695,7 b' function! s:HGAnnotate(...)'
691 return -1
695 return -1
692 endif
696 endif
693
697
694 let resultBuffer=s:HGDoCommand('annotate -ndu -r ' . revision, 'hgannotate', revision)
698 let resultBuffer=<SID>HGDoCommand('annotate -ndu -r ' . revision, 'hgannotate', revision)
695 "echomsg "DBG: ".resultBuffer
699 "echomsg "DBG: ".resultBuffer
696 if resultBuffer != -1
700 if resultBuffer != -1
697 set filetype=HGAnnotate
701 set filetype=HGAnnotate
@@ -706,10 +710,10 b' function! s:HGCommit(...)'
706 " is used; if bang is supplied, an empty message is used; otherwise, the
710 " is used; if bang is supplied, an empty message is used; otherwise, the
707 " user is provided a buffer from which to edit the commit message.
711 " user is provided a buffer from which to edit the commit message.
708 if a:2 != "" || a:1 == "!"
712 if a:2 != "" || a:1 == "!"
709 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('commit -m "' . a:2 . '"', 'hgcommit', ''))
713 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('commit -m "' . a:2 . '"', 'hgcommit', ''))
710 endif
714 endif
711
715
712 let hgBufferCheck=s:HGCurrentBufferCheck()
716 let hgBufferCheck=<SID>HGCurrentBufferCheck()
713 if hgBufferCheck == -1
717 if hgBufferCheck == -1
714 echo "Original buffer no longer exists, aborting."
718 echo "Original buffer no longer exists, aborting."
715 return -1
719 return -1
@@ -725,7 +729,7 b' function! s:HGCommit(...)'
725 let messageFileName = tempname()
729 let messageFileName = tempname()
726
730
727 let fileName=bufname(hgBufferCheck)
731 let fileName=bufname(hgBufferCheck)
728 let realFilePath=s:HGResolveLink(fileName)
732 let realFilePath=<SID>HGResolveLink(fileName)
729 let newCwd=fnamemodify(realFilePath, ':h')
733 let newCwd=fnamemodify(realFilePath, ':h')
730 if strlen(newCwd) == 0
734 if strlen(newCwd) == 0
731 " Account for autochdir being in effect, which will make this blank, but
735 " Account for autochdir being in effect, which will make this blank, but
@@ -735,7 +739,7 b' function! s:HGCommit(...)'
735
739
736 let realFileName=fnamemodify(realFilePath, ':t')
740 let realFileName=fnamemodify(realFilePath, ':t')
737
741
738 if s:HGEditFile(messageFileName, hgBufferCheck) == -1
742 if <SID>HGEditFile(messageFileName, hgBufferCheck) == -1
739 return
743 return
740 endif
744 endif
741
745
@@ -766,9 +770,9 b' function! s:HGCommit(...)'
766 silent put =\"HG: Enter Log. Lines beginning with `HG:' are removed automatically\"
770 silent put =\"HG: Enter Log. Lines beginning with `HG:' are removed automatically\"
767 silent put ='HG: Type <leader>cc (or your own <Plug>HGCommit mapping)'
771 silent put ='HG: Type <leader>cc (or your own <Plug>HGCommit mapping)'
768
772
769 if s:HGGetOption('HGCommandCommitOnWrite', 1) == 1
773 if <SID>HGGetOption('HGCommandCommitOnWrite', 1) == 1
770 execute 'au HGCommit BufWritePre' autoPattern 'g/^HG:/d'
774 execute 'au HGCommit BufWritePre' autoPattern 'g/^HG:/d'
771 execute 'au HGCommit BufWritePost' autoPattern 'call s:HGFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . hgBufferCheck . ') | au! * ' autoPattern
775 execute 'au HGCommit BufWritePost' autoPattern 'call <SID>HGFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . hgBufferCheck . ') | au! * ' autoPattern
772 silent put ='HG: or write this buffer'
776 silent put ='HG: or write this buffer'
773 endif
777 endif
774
778
@@ -797,7 +801,7 b' function! s:HGDiff(...)'
797 let caption = ''
801 let caption = ''
798 endif
802 endif
799
803
800 let hgdiffopt=s:HGGetOption('HGCommandDiffOpt', 'w')
804 let hgdiffopt=<SID>HGGetOption('HGCommandDiffOpt', 'w')
801
805
802 if hgdiffopt == ""
806 if hgdiffopt == ""
803 let diffoptionstring=""
807 let diffoptionstring=""
@@ -805,8 +809,8 b' function! s:HGDiff(...)'
805 let diffoptionstring=" -" . hgdiffopt . " "
809 let diffoptionstring=" -" . hgdiffopt . " "
806 endif
810 endif
807
811
808 let resultBuffer = s:HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption)
812 let resultBuffer = <SID>HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption)
809 if resultBuffer != -1
813 if resultBuffer != -1
810 set filetype=diff
814 set filetype=diff
811 endif
815 endif
812 return resultBuffer
816 return resultBuffer
@@ -815,7 +819,7 b' endfunction'
815
819
816 " Function: s:HGGotoOriginal(["!]) {{{2
820 " Function: s:HGGotoOriginal(["!]) {{{2
817 function! s:HGGotoOriginal(...)
821 function! s:HGGotoOriginal(...)
818 let origBuffNR = s:HGCurrentBufferCheck()
822 let origBuffNR = <SID>HGCurrentBufferCheck()
819 if origBuffNR > 0
823 if origBuffNR > 0
820 let origWinNR = bufwinnr(origBuffNR)
824 let origWinNR = bufwinnr(origBuffNR)
821 if origWinNR == -1
825 if origWinNR == -1
@@ -845,11 +849,11 b' function! s:HGFinishCommit(messageFile, '
845 if strlen(a:targetDir) > 0
849 if strlen(a:targetDir) > 0
846 execute 'cd' escape(a:targetDir, ' ')
850 execute 'cd' escape(a:targetDir, ' ')
847 endif
851 endif
848 let resultBuffer=s:HGCreateCommandBuffer('commit -l "' . a:messageFile . '" "'. a:targetFile . '"', 'hgcommit', '', a:origBuffNR)
852 let resultBuffer=<SID>HGCreateCommandBuffer('commit -l "' . a:messageFile . '" "'. a:targetFile . '"', 'hgcommit', '', a:origBuffNR)
849 execute 'cd' escape(oldCwd, ' ')
853 execute 'cd' escape(oldCwd, ' ')
850 execute 'bw' escape(a:messageFile, ' *?\')
854 execute 'bw' escape(a:messageFile, ' *?\')
851 silent execute 'call delete("' . a:messageFile . '")'
855 silent execute 'call delete("' . a:messageFile . '")'
852 return s:HGMarkOrigBufferForSetup(resultBuffer)
856 return <SID>HGMarkOrigBufferForSetup(resultBuffer)
853 else
857 else
854 echoerr "Can't read message file; no commit is possible."
858 echoerr "Can't read message file; no commit is possible."
855 return -1
859 return -1
@@ -866,7 +870,7 b' function! s:HGLog(...)'
866 let caption = a:1
870 let caption = a:1
867 endif
871 endif
868
872
869 let resultBuffer=s:HGDoCommand('log' . versionOption, 'hglog', caption)
873 let resultBuffer=<SID>HGDoCommand('log' . versionOption, 'hglog', caption)
870 if resultBuffer != ""
874 if resultBuffer != ""
871 set filetype=rcslog
875 set filetype=rcslog
872 endif
876 endif
@@ -875,14 +879,14 b' endfunction'
875
879
876 " Function: s:HGRevert() {{{2
880 " Function: s:HGRevert() {{{2
877 function! s:HGRevert()
881 function! s:HGRevert()
878 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('revert', 'hgrevert', ''))
882 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('revert', 'hgrevert', ''))
879 endfunction
883 endfunction
880
884
881 " Function: s:HGReview(...) {{{2
885 " Function: s:HGReview(...) {{{2
882 function! s:HGReview(...)
886 function! s:HGReview(...)
883 if a:0 == 0
887 if a:0 == 0
884 let versiontag=""
888 let versiontag=""
885 if s:HGGetOption('HGCommandInteractive', 0)
889 if <SID>HGGetOption('HGCommandInteractive', 0)
886 let versiontag=input('Revision: ')
890 let versiontag=input('Revision: ')
887 endif
891 endif
888 if versiontag == ""
892 if versiontag == ""
@@ -896,7 +900,7 b' function! s:HGReview(...)'
896 let versionOption=" -r " . versiontag . " "
900 let versionOption=" -r " . versiontag . " "
897 endif
901 endif
898
902
899 let resultBuffer = s:HGDoCommand('cat' . versionOption, 'hgreview', versiontag)
903 let resultBuffer = <SID>HGDoCommand('cat' . versionOption, 'hgreview', versiontag)
900 if resultBuffer > 0
904 if resultBuffer > 0
901 let &filetype=getbufvar(b:HGOrigBuffNR, '&filetype')
905 let &filetype=getbufvar(b:HGOrigBuffNR, '&filetype')
902 endif
906 endif
@@ -906,18 +910,18 b' endfunction'
906
910
907 " Function: s:HGStatus() {{{2
911 " Function: s:HGStatus() {{{2
908 function! s:HGStatus()
912 function! s:HGStatus()
909 return s:HGDoCommand('status', 'hgstatus', '')
913 return <SID>HGDoCommand('status', 'hgstatus', '')
910 endfunction
914 endfunction
911
915
912
916
913 " Function: s:HGUpdate() {{{2
917 " Function: s:HGUpdate() {{{2
914 function! s:HGUpdate()
918 function! s:HGUpdate()
915 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('update', 'update', ''))
919 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('update', 'update', ''))
916 endfunction
920 endfunction
917
921
918 " Function: s:HGVimDiff(...) {{{2
922 " Function: s:HGVimDiff(...) {{{2
919 function! s:HGVimDiff(...)
923 function! s:HGVimDiff(...)
920 let originalBuffer = s:HGCurrentBufferCheck()
924 let originalBuffer = <SID>HGCurrentBufferCheck()
921 let s:HGCommandEditFileRunning = s:HGCommandEditFileRunning + 1
925 let s:HGCommandEditFileRunning = s:HGCommandEditFileRunning + 1
922 try
926 try
923 " If there's already a VimDiff'ed window, restore it.
927 " If there's already a VimDiff'ed window, restore it.
@@ -925,16 +929,16 b' function! s:HGVimDiff(...)'
925
929
926 if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer
930 if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer
927 " Clear the existing vimdiff setup by removing the result buffers.
931 " Clear the existing vimdiff setup by removing the result buffers.
928 call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
932 call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
929 endif
933 endif
930
934
931 " Split and diff
935 " Split and diff
932 if(a:0 == 2)
936 if(a:0 == 2)
933 " Reset the vimdiff system, as 2 explicit versions were provided.
937 " Reset the vimdiff system, as 2 explicit versions were provided.
934 if exists('s:vimDiffSourceBuffer')
938 if exists('s:vimDiffSourceBuffer')
935 call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
939 call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
936 endif
940 endif
937 let resultBuffer = s:HGReview(a:1)
941 let resultBuffer = <SID>HGReview(a:1)
938 if resultBuffer < 0
942 if resultBuffer < 0
939 echomsg "Can't open HG revision " . a:1
943 echomsg "Can't open HG revision " . a:1
940 return resultBuffer
944 return resultBuffer
@@ -945,10 +949,10 b' function! s:HGVimDiff(...)'
945 let s:vimDiffScratchList = '{'. resultBuffer . '}'
949 let s:vimDiffScratchList = '{'. resultBuffer . '}'
946 " If no split method is defined, cheat, and set it to vertical.
950 " If no split method is defined, cheat, and set it to vertical.
947 try
951 try
948 call s:HGOverrideOption('HGCommandSplit', s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical')))
952 call <SID>HGOverrideOption('HGCommandSplit', <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical')))
949 let resultBuffer=s:HGReview(a:2)
953 let resultBuffer=<SID>HGReview(a:2)
950 finally
954 finally
951 call s:HGOverrideOption('HGCommandSplit')
955 call <SID>HGOverrideOption('HGCommandSplit')
952 endtry
956 endtry
953 if resultBuffer < 0
957 if resultBuffer < 0
954 echomsg "Can't open HG revision " . a:1
958 echomsg "Can't open HG revision " . a:1
@@ -962,16 +966,16 b' function! s:HGVimDiff(...)'
962 " Add new buffer
966 " Add new buffer
963 try
967 try
964 " Force splitting behavior, otherwise why use vimdiff?
968 " Force splitting behavior, otherwise why use vimdiff?
965 call s:HGOverrideOption("HGCommandEdit", "split")
969 call <SID>HGOverrideOption("HGCommandEdit", "split")
966 call s:HGOverrideOption("HGCommandSplit", s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical')))
970 call <SID>HGOverrideOption("HGCommandSplit", <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical')))
967 if(a:0 == 0)
971 if(a:0 == 0)
968 let resultBuffer=s:HGReview()
972 let resultBuffer=<SID>HGReview()
969 else
973 else
970 let resultBuffer=s:HGReview(a:1)
974 let resultBuffer=<SID>HGReview(a:1)
971 endif
975 endif
972 finally
976 finally
973 call s:HGOverrideOption("HGCommandEdit")
977 call <SID>HGOverrideOption("HGCommandEdit")
974 call s:HGOverrideOption("HGCommandSplit")
978 call <SID>HGOverrideOption("HGCommandSplit")
975 endtry
979 endtry
976 if resultBuffer < 0
980 if resultBuffer < 0
977 echomsg "Can't open current HG revision"
981 echomsg "Can't open current HG revision"
@@ -990,14 +994,14 b' function! s:HGVimDiff(...)'
990 wincmd W
994 wincmd W
991 execute 'buffer' originalBuffer
995 execute 'buffer' originalBuffer
992 " Store info for later original buffer restore
996 " Store info for later original buffer restore
993 let s:vimDiffRestoreCmd =
997 let s:vimDiffRestoreCmd =
994 \ "call setbufvar(".originalBuffer.", \"&diff\", ".getbufvar(originalBuffer, '&diff').")"
998 \ "call setbufvar(".originalBuffer.", \"&diff\", ".getbufvar(originalBuffer, '&diff').")"
995 \ . "|call setbufvar(".originalBuffer.", \"&foldcolumn\", ".getbufvar(originalBuffer, '&foldcolumn').")"
999 \ . "|call setbufvar(".originalBuffer.", \"&foldcolumn\", ".getbufvar(originalBuffer, '&foldcolumn').")"
996 \ . "|call setbufvar(".originalBuffer.", \"&foldenable\", ".getbufvar(originalBuffer, '&foldenable').")"
1000 \ . "|call setbufvar(".originalBuffer.", \"&foldenable\", ".getbufvar(originalBuffer, '&foldenable').")"
997 \ . "|call setbufvar(".originalBuffer.", \"&foldmethod\", '".getbufvar(originalBuffer, '&foldmethod')."')"
1001 \ . "|call setbufvar(".originalBuffer.", \"&foldmethod\", '".getbufvar(originalBuffer, '&foldmethod')."')"
998 \ . "|call setbufvar(".originalBuffer.", \"&scrollbind\", ".getbufvar(originalBuffer, '&scrollbind').")"
1002 \ . "|call setbufvar(".originalBuffer.", \"&scrollbind\", ".getbufvar(originalBuffer, '&scrollbind').")"
999 \ . "|call setbufvar(".originalBuffer.", \"&wrap\", ".getbufvar(originalBuffer, '&wrap').")"
1003 \ . "|call setbufvar(".originalBuffer.", \"&wrap\", ".getbufvar(originalBuffer, '&wrap').")"
1000 \ . "|if &foldmethod=='manual'|execute 'normal zE'|endif"
1004 \ . "|if &foldmethod=='manual'|execute 'normal! zE'|endif"
1001 diffthis
1005 diffthis
1002 wincmd w
1006 wincmd w
1003 else
1007 else
@@ -1027,17 +1031,17 b' endfunction'
1027
1031
1028 " Section: Command definitions {{{1
1032 " Section: Command definitions {{{1
1029 " Section: Primary commands {{{2
1033 " Section: Primary commands {{{2
1030 com! HGAdd call s:HGAdd()
1034 com! HGAdd call <SID>HGAdd()
1031 com! -nargs=? HGAnnotate call s:HGAnnotate(<f-args>)
1035 com! -nargs=? HGAnnotate call <SID>HGAnnotate(<f-args>)
1032 com! -bang -nargs=? HGCommit call s:HGCommit(<q-bang>, <q-args>)
1036 com! -bang -nargs=? HGCommit call <SID>HGCommit(<q-bang>, <q-args>)
1033 com! -nargs=* HGDiff call s:HGDiff(<f-args>)
1037 com! -nargs=* HGDiff call <SID>HGDiff(<f-args>)
1034 com! -bang HGGotoOriginal call s:HGGotoOriginal(<q-bang>)
1038 com! -bang HGGotoOriginal call <SID>HGGotoOriginal(<q-bang>)
1035 com! -nargs=? HGLog call s:HGLog(<f-args>)
1039 com! -nargs=? HGLog call <SID>HGLog(<f-args>)
1036 com! HGRevert call s:HGRevert()
1040 com! HGRevert call <SID>HGRevert()
1037 com! -nargs=? HGReview call s:HGReview(<f-args>)
1041 com! -nargs=? HGReview call <SID>HGReview(<f-args>)
1038 com! HGStatus call s:HGStatus()
1042 com! HGStatus call <SID>HGStatus()
1039 com! HGUpdate call s:HGUpdate()
1043 com! HGUpdate call <SID>HGUpdate()
1040 com! -nargs=* HGVimDiff call s:HGVimDiff(<f-args>)
1044 com! -nargs=* HGVimDiff call <SID>HGVimDiff(<f-args>)
1041
1045
1042 " Section: HG buffer management commands {{{2
1046 " Section: HG buffer management commands {{{2
1043 com! HGDisableBufferSetup call HGDisableBufferSetup()
1047 com! HGDisableBufferSetup call HGDisableBufferSetup()
@@ -1173,7 +1177,7 b' endfunction'
1173
1177
1174 augroup HGVimDiffRestore
1178 augroup HGVimDiffRestore
1175 au!
1179 au!
1176 au BufUnload * call s:HGVimDiffRestore(expand("<abuf>"))
1180 au BufUnload * call <SID>HGVimDiffRestore(expand("<abuf>"))
1177 augroup END
1181 augroup END
1178
1182
1179 " Section: Optional activation of buffer management {{{1
1183 " Section: Optional activation of buffer management {{{1
@@ -1183,20 +1187,24 b" if s:HGGetOption('HGCommandEnableBufferS"
1183 endif
1187 endif
1184
1188
1185 " Section: Doc installation {{{1
1189 " Section: Doc installation {{{1
1186 "
1187 let s:revision="0.1"
1188 silent! let s:install_status =
1189 \ s:HGInstallDocumentation(expand('<sfile>:p'), s:revision)
1190 if (s:install_status == 1)
1191 echom expand("<sfile>:t:r") . ' v' . s:revision .
1192 \ ': Help-documentation installed.'
1193 endif
1194
1190
1191 if <SID>HGInstallDocumentation(expand("<sfile>:p"))
1192 echomsg s:script_name s:script_version . ": updated documentation"
1193 endif
1195
1194
1196 " Section: Plugin completion {{{1
1195 " Section: Plugin completion {{{1
1197
1196
1197 " delete one-time vars and functions
1198 delfunction <SID>HGInstallDocumentation
1199 delfunction <SID>HGFlexiMkdir
1200 delfunction <SID>HGCleanupOnFailure
1201 unlet s:script_version s:script_name
1202
1198 let loaded_hgcommand=2
1203 let loaded_hgcommand=2
1199 silent do HGCommand User HGPluginFinish
1204 silent do HGCommand User HGPluginFinish
1205
1206 let &cpo = s:save_cpo
1207 unlet s:save_cpo
1200 " vim:se expandtab sts=2 sw=2:
1208 " vim:se expandtab sts=2 sw=2:
1201 finish
1209 finish
1202
1210
@@ -1228,16 +1236,16 b' 1. Contents\t\t\t\t\t\t *hgcommand-contents*'
1228 ==============================================================================
1236 ==============================================================================
1229 2. HGCommand Installation *hgcommand-install*
1237 2. HGCommand Installation *hgcommand-install*
1230
1238
1231 In order to install the plugin, place the hgcommand.vim file into a plugin'
1239 In order to install the plugin, place the hgcommand.vim file into a plugin'
1232 directory in your runtime path (please see |add-global-plugin| and
1240 directory in your runtime path (please see |add-global-plugin| and
1233 |'runtimepath'|.
1241 |'runtimepath'|.
1234
1242
1235 HGCommand may be customized by setting variables, creating maps, and
1243 HGCommand may be customized by setting variables, creating maps, and
1236 specifying event handlers. Please see |hgcommand-customize| for more
1244 specifying event handlers. Please see |hgcommand-customize| for more
1237 details.
1245 details.
1238
1246
1239 *hgcommand-auto-help*
1247 *hgcommand-auto-help*
1240 The help file is automagically generated when the |hgcommand| script is
1248 The help file is automagically generated when the |hgcommand| script is
1241 loaded for the first time.
1249 loaded for the first time.
1242
1250
1243 ==============================================================================
1251 ==============================================================================
@@ -1245,32 +1253,32 b' 2. HGCommand Installation\t\t\t\t *hgcomma'
1245 3. HGCommand Intro *hgcommand*
1253 3. HGCommand Intro *hgcommand*
1246 *hgcommand-intro*
1254 *hgcommand-intro*
1247
1255
1248 The HGCommand plugin provides global ex commands for manipulating
1256 The HGCommand plugin provides global ex commands for manipulating
1249 HG-controlled source files. In general, each command operates on the
1257 HG-controlled source files. In general, each command operates on the
1250 current buffer and accomplishes a separate hg function, such as update,
1258 current buffer and accomplishes a separate hg function, such as update,
1251 commit, log, and others (please see |hgcommand-commands| for a list of all
1259 commit, log, and others (please see |hgcommand-commands| for a list of all
1252 available commands). The results of each operation are displayed in a
1260 available commands). The results of each operation are displayed in a
1253 scratch buffer. Several buffer variables are defined for those scratch
1261 scratch buffer. Several buffer variables are defined for those scratch
1254 buffers (please see |hgcommand-buffer-variables|).
1262 buffers (please see |hgcommand-buffer-variables|).
1255
1263
1256 The notion of "current file" means either the current buffer, or, in the
1264 The notion of "current file" means either the current buffer, or, in the
1257 case of a directory buffer, the file on the current line within the buffer.
1265 case of a directory buffer, the file on the current line within the buffer.
1258
1266
1259 For convenience, any HGCommand invoked on a HGCommand scratch buffer acts
1267 For convenience, any HGCommand invoked on a HGCommand scratch buffer acts
1260 as though it was invoked on the original file and splits the screen so that
1268 as though it was invoked on the original file and splits the screen so that
1261 the output appears in a new window.
1269 the output appears in a new window.
1262
1270
1263 Many of the commands accept revisions as arguments. By default, most
1271 Many of the commands accept revisions as arguments. By default, most
1264 operate on the most recent revision on the current branch if no revision is
1272 operate on the most recent revision on the current branch if no revision is
1265 specified (though see |HGCommandInteractive| to prompt instead).
1273 specified (though see |HGCommandInteractive| to prompt instead).
1266
1274
1267 Each HGCommand is mapped to a key sequence starting with the <Leader>
1275 Each HGCommand is mapped to a key sequence starting with the <Leader>
1268 keystroke. The default mappings may be overridden by supplying different
1276 keystroke. The default mappings may be overridden by supplying different
1269 mappings before the plugin is loaded, such as in the vimrc, in the standard
1277 mappings before the plugin is loaded, such as in the vimrc, in the standard
1270 fashion for plugin mappings. For examples, please see
1278 fashion for plugin mappings. For examples, please see
1271 |hgcommand-mappings-override|.
1279 |hgcommand-mappings-override|.
1272
1280
1273 The HGCommand plugin may be configured in several ways. For more details,
1281 The HGCommand plugin may be configured in several ways. For more details,
1274 please see |hgcommand-customize|.
1282 please see |hgcommand-customize|.
1275
1283
1276 ==============================================================================
1284 ==============================================================================
@@ -1294,85 +1302,85 b' 4.1 HGCommand commands\t\t\t\t\t *hgcommand-'
1294
1302
1295 :HGAdd *:HGAdd*
1303 :HGAdd *:HGAdd*
1296
1304
1297 This command performs "hg add" on the current file. Please note, this does
1305 This command performs "hg add" on the current file. Please note, this does
1298 not commit the newly-added file.
1306 not commit the newly-added file.
1299
1307
1300 :HGAnnotate *:HGAnnotate*
1308 :HGAnnotate *:HGAnnotate*
1301
1309
1302 This command performs "hg annotate" on the current file. If an argument is
1310 This command performs "hg annotate" on the current file. If an argument is
1303 given, the argument is used as a revision number to display. If not given
1311 given, the argument is used as a revision number to display. If not given
1304 an argument, it uses the most recent version of the file on the current
1312 an argument, it uses the most recent version of the file on the current
1305 branch. Additionally, if the current buffer is a HGAnnotate buffer
1313 branch. Additionally, if the current buffer is a HGAnnotate buffer
1306 already, the version number on the current line is used.
1314 already, the version number on the current line is used.
1307
1315
1308 If the |HGCommandAnnotateParent| variable is set to a non-zero value, the
1316 If the |HGCommandAnnotateParent| variable is set to a non-zero value, the
1309 version previous to the one on the current line is used instead. This
1317 version previous to the one on the current line is used instead. This
1310 allows one to navigate back to examine the previous version of a line.
1318 allows one to navigate back to examine the previous version of a line.
1311
1319
1312 The filetype of the HGCommand scratch buffer is set to 'HGAnnotate', to
1320 The filetype of the HGCommand scratch buffer is set to 'HGAnnotate', to
1313 take advantage of the bundled syntax file.
1321 take advantage of the bundled syntax file.
1314
1322
1315
1323
1316 :HGCommit[!] *:HGCommit*
1324 :HGCommit[!] *:HGCommit*
1317
1325
1318 If called with arguments, this performs "hg commit" using the arguments as
1326 If called with arguments, this performs "hg commit" using the arguments as
1319 the log message.
1327 the log message.
1320
1328
1321 If '!' is used with no arguments, an empty log message is committed.
1329 If '!' is used with no arguments, an empty log message is committed.
1322
1330
1323 If called with no arguments, this is a two-step command. The first step
1331 If called with no arguments, this is a two-step command. The first step
1324 opens a buffer to accept a log message. When that buffer is written, it is
1332 opens a buffer to accept a log message. When that buffer is written, it is
1325 automatically closed and the file is committed using the information from
1333 automatically closed and the file is committed using the information from
1326 that log message. The commit can be abandoned if the log message buffer is
1334 that log message. The commit can be abandoned if the log message buffer is
1327 deleted or wiped before being written.
1335 deleted or wiped before being written.
1328
1336
1329 Alternatively, the mapping that is used to invoke :HGCommit (by default
1337 Alternatively, the mapping that is used to invoke :HGCommit (by default
1330 <Leader>hgc) can be used in the log message buffer to immediately commit.
1338 <Leader>hgc) can be used in the log message buffer to immediately commit.
1331 This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to
1339 This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to
1332 disable the normal commit-on-write behavior.
1340 disable the normal commit-on-write behavior.
1333
1341
1334 :HGDiff *:HGDiff*
1342 :HGDiff *:HGDiff*
1335
1343
1336 With no arguments, this performs "hg diff" on the current file against the
1344 With no arguments, this performs "hg diff" on the current file against the
1337 current repository version.
1345 current repository version.
1338
1346
1339 With one argument, "hg diff" is performed on the current file against the
1347 With one argument, "hg diff" is performed on the current file against the
1340 specified revision.
1348 specified revision.
1341
1349
1342 With two arguments, hg diff is performed between the specified revisions of
1350 With two arguments, hg diff is performed between the specified revisions of
1343 the current file.
1351 the current file.
1344
1352
1345 This command uses the 'HGCommandDiffOpt' variable to specify diff options.
1353 This command uses the 'HGCommandDiffOpt' variable to specify diff options.
1346 If that variable does not exist, then 'wbBc' is assumed. If you wish to
1354 If that variable does not exist, then 'wbBc' is assumed. If you wish to
1347 have no options, then set it to the empty string.
1355 have no options, then set it to the empty string.
1348
1356
1349
1357
1350 :HGGotoOriginal *:HGGotoOriginal*
1358 :HGGotoOriginal *:HGGotoOriginal*
1351
1359
1352 This command returns the current window to the source buffer, if the
1360 This command returns the current window to the source buffer, if the
1353 current buffer is a HG command output buffer.
1361 current buffer is a HG command output buffer.
1354
1362
1355 :HGGotoOriginal!
1363 :HGGotoOriginal!
1356
1364
1357 Like ":HGGotoOriginal" but also executes :bufwipeout on all HG command
1365 Like ":HGGotoOriginal" but also executes :bufwipeout on all HG command
1358 output buffers for the source buffer.
1366 output buffers for the source buffer.
1359
1367
1360 :HGLog *:HGLog*
1368 :HGLog *:HGLog*
1361
1369
1362 Performs "hg log" on the current file.
1370 Performs "hg log" on the current file.
1363
1371
1364 If an argument is given, it is passed as an argument to the "-r" option of
1372 If an argument is given, it is passed as an argument to the "-r" option of
1365 "hg log".
1373 "hg log".
1366
1374
1367 :HGRevert *:HGRevert*
1375 :HGRevert *:HGRevert*
1368
1376
1369 Replaces the current file with the most recent version from the repository
1377 Replaces the current file with the most recent version from the repository
1370 in order to wipe out any undesired changes.
1378 in order to wipe out any undesired changes.
1371
1379
1372 :HGReview *:HGReview*
1380 :HGReview *:HGReview*
1373
1381
1374 Retrieves a particular version of the current file. If no argument is
1382 Retrieves a particular version of the current file. If no argument is
1375 given, the most recent version of the file on the current branch is
1383 given, the most recent version of the file on the current branch is
1376 retrieved. Otherwise, the specified version is retrieved.
1384 retrieved. Otherwise, the specified version is retrieved.
1377
1385
1378 :HGStatus *:HGStatus*
1386 :HGStatus *:HGStatus*
@@ -1381,37 +1389,37 b' 4.1 HGCommand commands\t\t\t\t\t *hgcommand-'
1381
1389
1382 :HGUpdate *:HGUpdate*
1390 :HGUpdate *:HGUpdate*
1383
1391
1384 Performs "hg update" on the current file. This intentionally does not
1392 Performs "hg update" on the current file. This intentionally does not
1385 automatically reload the current buffer, though vim should prompt the user
1393 automatically reload the current buffer, though vim should prompt the user
1386 to do so if the underlying file is altered by this command.
1394 to do so if the underlying file is altered by this command.
1387
1395
1388 :HGVimDiff *:HGVimDiff*
1396 :HGVimDiff *:HGVimDiff*
1389
1397
1390 With no arguments, this prompts the user for a revision and then uses
1398 With no arguments, this prompts the user for a revision and then uses
1391 vimdiff to display the differences between the current file and the
1399 vimdiff to display the differences between the current file and the
1392 specified revision. If no revision is specified, the most recent version
1400 specified revision. If no revision is specified, the most recent version
1393 of the file on the current branch is used.
1401 of the file on the current branch is used.
1394
1402
1395 With one argument, that argument is used as the revision as above. With
1403 With one argument, that argument is used as the revision as above. With
1396 two arguments, the differences between the two revisions is displayed using
1404 two arguments, the differences between the two revisions is displayed using
1397 vimdiff.
1405 vimdiff.
1398
1406
1399 With either zero or one argument, the original buffer is used to perform
1407 With either zero or one argument, the original buffer is used to perform
1400 the vimdiff. When the other buffer is closed, the original buffer will be
1408 the vimdiff. When the other buffer is closed, the original buffer will be
1401 returned to normal mode.
1409 returned to normal mode.
1402
1410
1403 Once vimdiff mode is started using the above methods, additional vimdiff
1411 Once vimdiff mode is started using the above methods, additional vimdiff
1404 buffers may be added by passing a single version argument to the command.
1412 buffers may be added by passing a single version argument to the command.
1405 There may be up to 4 vimdiff buffers total.
1413 There may be up to 4 vimdiff buffers total.
1406
1414
1407 Using the 2-argument form of the command resets the vimdiff to only those 2
1415 Using the 2-argument form of the command resets the vimdiff to only those 2
1408 versions. Additionally, invoking the command on a different file will
1416 versions. Additionally, invoking the command on a different file will
1409 close the previous vimdiff buffers.
1417 close the previous vimdiff buffers.
1410
1418
1411
1419
1412 4.2 Mappings *hgcommand-mappings*
1420 4.2 Mappings *hgcommand-mappings*
1413
1421
1414 By default, a mapping is defined for each command. These mappings execute
1422 By default, a mapping is defined for each command. These mappings execute
1415 the default (no-argument) form of each command.
1423 the default (no-argument) form of each command.
1416
1424
1417 <Leader>hga HGAdd
1425 <Leader>hga HGAdd
@@ -1428,20 +1436,20 b' 4.2 Mappings\t\t\t\t\t\t *hgcommand-mappings*'
1428
1436
1429 *hgcommand-mappings-override*
1437 *hgcommand-mappings-override*
1430
1438
1431 The default mappings can be overriden by user-provided instead by mapping
1439 The default mappings can be overriden by user-provided instead by mapping
1432 to <Plug>CommandName. This is especially useful when these mappings
1440 to <Plug>CommandName. This is especially useful when these mappings
1433 collide with other existing mappings (vim will warn of this during plugin
1441 collide with other existing mappings (vim will warn of this during plugin
1434 initialization, but will not clobber the existing mappings).
1442 initialization, but will not clobber the existing mappings).
1435
1443
1436 For instance, to override the default mapping for :HGAdd to set it to
1444 For instance, to override the default mapping for :HGAdd to set it to
1437 '\add', add the following to the vimrc: >
1445 '\add', add the following to the vimrc: >
1438
1446
1439 nmap \add <Plug>HGAdd
1447 nmap \add <Plug>HGAdd
1440 <
1448 <
1441 4.3 Automatic buffer variables *hgcommand-buffer-variables*
1449 4.3 Automatic buffer variables *hgcommand-buffer-variables*
1442
1450
1443 Several buffer variables are defined in each HGCommand result buffer.
1451 Several buffer variables are defined in each HGCommand result buffer.
1444 These may be useful for additional customization in callbacks defined in
1452 These may be useful for additional customization in callbacks defined in
1445 the event handlers (please see |hgcommand-events|).
1453 the event handlers (please see |hgcommand-events|).
1446
1454
1447 The following variables are automatically defined:
1455 The following variables are automatically defined:
@@ -1452,24 +1460,24 b' b:hgOrigBuffNR\t\t\t\t\t\t *b:hgOrigBuffN'
1452
1460
1453 b:hgcmd *b:hgcmd*
1461 b:hgcmd *b:hgcmd*
1454
1462
1455 This variable is set to the name of the hg command that created the result
1463 This variable is set to the name of the hg command that created the result
1456 buffer.
1464 buffer.
1457 ==============================================================================
1465 ==============================================================================
1458
1466
1459 5. Configuration and customization *hgcommand-customize*
1467 5. Configuration and customization *hgcommand-customize*
1460 *hgcommand-config*
1468 *hgcommand-config*
1461
1469
1462 The HGCommand plugin can be configured in two ways: by setting
1470 The HGCommand plugin can be configured in two ways: by setting
1463 configuration variables (see |hgcommand-options|) or by defining HGCommand
1471 configuration variables (see |hgcommand-options|) or by defining HGCommand
1464 event handlers (see |hgcommand-events|). Additionally, the HGCommand
1472 event handlers (see |hgcommand-events|). Additionally, the HGCommand
1465 plugin provides several option for naming the HG result buffers (see
1473 plugin provides several option for naming the HG result buffers (see
1466 |hgcommand-naming|) and supported a customized status line (see
1474 |hgcommand-naming|) and supported a customized status line (see
1467 |hgcommand-statusline| and |hgcommand-buffer-management|).
1475 |hgcommand-statusline| and |hgcommand-buffer-management|).
1468
1476
1469 5.1 HGCommand configuration variables *hgcommand-options*
1477 5.1 HGCommand configuration variables *hgcommand-options*
1470
1478
1471 Several variables affect the plugin's behavior. These variables are
1479 Several variables affect the plugin's behavior. These variables are
1472 checked at time of execution, and may be defined at the window, buffer, or
1480 checked at time of execution, and may be defined at the window, buffer, or
1473 global level and are checked in that order of precedence.
1481 global level and are checked in that order of precedence.
1474
1482
1475
1483
@@ -1490,87 +1498,87 b' 5.1 HGCommand configuration variables\t\t\t'
1490
1498
1491 HGCommandAnnotateParent *HGCommandAnnotateParent*
1499 HGCommandAnnotateParent *HGCommandAnnotateParent*
1492
1500
1493 This variable, if set to a non-zero value, causes the zero-argument form of
1501 This variable, if set to a non-zero value, causes the zero-argument form of
1494 HGAnnotate when invoked on a HGAnnotate buffer to go to the version
1502 HGAnnotate when invoked on a HGAnnotate buffer to go to the version
1495 previous to that displayed on the current line. If not set, it defaults to
1503 previous to that displayed on the current line. If not set, it defaults to
1496 0.
1504 0.
1497
1505
1498 HGCommandCommitOnWrite *HGCommandCommitOnWrite*
1506 HGCommandCommitOnWrite *HGCommandCommitOnWrite*
1499
1507
1500 This variable, if set to a non-zero value, causes the pending hg commit to
1508 This variable, if set to a non-zero value, causes the pending hg commit to
1501 take place immediately as soon as the log message buffer is written. If
1509 take place immediately as soon as the log message buffer is written. If
1502 set to zero, only the HGCommit mapping will cause the pending commit to
1510 set to zero, only the HGCommit mapping will cause the pending commit to
1503 occur. If not set, it defaults to 1.
1511 occur. If not set, it defaults to 1.
1504
1512
1505 HGCommandHGExec *HGCommandHGExec*
1513 HGCommandHGExec *HGCommandHGExec*
1506
1514
1507 This variable controls the executable used for all HG commands. If not
1515 This variable controls the executable used for all HG commands. If not
1508 set, it defaults to "hg".
1516 set, it defaults to "hg".
1509
1517
1510 HGCommandDeleteOnHide *HGCommandDeleteOnHide*
1518 HGCommandDeleteOnHide *HGCommandDeleteOnHide*
1511
1519
1512 This variable, if set to a non-zero value, causes the temporary HG result
1520 This variable, if set to a non-zero value, causes the temporary HG result
1513 buffers to automatically delete themselves when hidden.
1521 buffers to automatically delete themselves when hidden.
1514
1522
1515 HGCommandDiffOpt *HGCommandDiffOpt*
1523 HGCommandDiffOpt *HGCommandDiffOpt*
1516
1524
1517 This variable, if set, determines the options passed to the diff command of
1525 This variable, if set, determines the options passed to the diff command of
1518 HG. If not set, it defaults to 'w'.
1526 HG. If not set, it defaults to 'w'.
1519
1527
1520 HGCommandDiffSplit *HGCommandDiffSplit*
1528 HGCommandDiffSplit *HGCommandDiffSplit*
1521
1529
1522 This variable overrides the |HGCommandSplit| variable, but only for buffers
1530 This variable overrides the |HGCommandSplit| variable, but only for buffers
1523 created with |:HGVimDiff|.
1531 created with |:HGVimDiff|.
1524
1532
1525 HGCommandEdit *HGCommandEdit*
1533 HGCommandEdit *HGCommandEdit*
1526
1534
1527 This variable controls whether the original buffer is replaced ('edit') or
1535 This variable controls whether the original buffer is replaced ('edit') or
1528 split ('split'). If not set, it defaults to 'edit'.
1536 split ('split'). If not set, it defaults to 'edit'.
1529
1537
1530 HGCommandEnableBufferSetup *HGCommandEnableBufferSetup*
1538 HGCommandEnableBufferSetup *HGCommandEnableBufferSetup*
1531
1539
1532 This variable, if set to a non-zero value, activates HG buffer management
1540 This variable, if set to a non-zero value, activates HG buffer management
1533 mode see (|hgcommand-buffer-management|). This mode means that three
1541 mode see (|hgcommand-buffer-management|). This mode means that three
1534 buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if
1542 buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if
1535 the file is HG-controlled. This is useful for displaying version
1543 the file is HG-controlled. This is useful for displaying version
1536 information in the status bar.
1544 information in the status bar.
1537
1545
1538 HGCommandInteractive *HGCommandInteractive*
1546 HGCommandInteractive *HGCommandInteractive*
1539
1547
1540 This variable, if set to a non-zero value, causes appropriate commands (for
1548 This variable, if set to a non-zero value, causes appropriate commands (for
1541 the moment, only |:HGReview|) to query the user for a revision to use
1549 the moment, only |:HGReview|) to query the user for a revision to use
1542 instead of the current revision if none is specified.
1550 instead of the current revision if none is specified.
1543
1551
1544 HGCommandNameMarker *HGCommandNameMarker*
1552 HGCommandNameMarker *HGCommandNameMarker*
1545
1553
1546 This variable, if set, configures the special attention-getting characters
1554 This variable, if set, configures the special attention-getting characters
1547 that appear on either side of the hg buffer type in the buffer name. This
1555 that appear on either side of the hg buffer type in the buffer name. This
1548 has no effect unless |HGCommandNameResultBuffers| is set to a true value.
1556 has no effect unless |HGCommandNameResultBuffers| is set to a true value.
1549 If not set, it defaults to '_'.
1557 If not set, it defaults to '_'.
1550
1558
1551 HGCommandNameResultBuffers *HGCommandNameResultBuffers*
1559 HGCommandNameResultBuffers *HGCommandNameResultBuffers*
1552
1560
1553 This variable, if set to a true value, causes the hg result buffers to be
1561 This variable, if set to a true value, causes the hg result buffers to be
1554 named in the old way ('<source file name> _<hg command>_'). If not set or
1562 named in the old way ('<source file name> _<hg command>_'). If not set or
1555 set to a false value, the result buffer is nameless.
1563 set to a false value, the result buffer is nameless.
1556
1564
1557 HGCommandSplit *HGCommandSplit*
1565 HGCommandSplit *HGCommandSplit*
1558
1566
1559 This variable controls the orientation of the various window splits that
1567 This variable controls the orientation of the various window splits that
1560 may occur (such as with HGVimDiff, when using a HG command on a HG command
1568 may occur (such as with HGVimDiff, when using a HG command on a HG command
1561 buffer, or when the |HGCommandEdit| variable is set to 'split'. If set to
1569 buffer, or when the |HGCommandEdit| variable is set to 'split'. If set to
1562 'horizontal', the resulting windows will be on stacked on top of one
1570 'horizontal', the resulting windows will be on stacked on top of one
1563 another. If set to 'vertical', the resulting windows will be side-by-side.
1571 another. If set to 'vertical', the resulting windows will be side-by-side.
1564 If not set, it defaults to 'horizontal' for all but HGVimDiff windows.
1572 If not set, it defaults to 'horizontal' for all but HGVimDiff windows.
1565
1573
1566 5.2 HGCommand events *hgcommand-events*
1574 5.2 HGCommand events *hgcommand-events*
1567
1575
1568 For additional customization, HGCommand can trigger user-defined events.
1576 For additional customization, HGCommand can trigger user-defined events.
1569 Event handlers are provided by defining User event autocommands (see
1577 Event handlers are provided by defining User event autocommands (see
1570 |autocommand|, |User|) in the HGCommand group with patterns matching the
1578 |autocommand|, |User|) in the HGCommand group with patterns matching the
1571 event name.
1579 event name.
1572
1580
1573 For instance, the following could be added to the vimrc to provide a 'q'
1581 For instance, the following could be added to the vimrc to provide a 'q'
1574 mapping to quit a HGCommand scratch buffer: >
1582 mapping to quit a HGCommand scratch buffer: >
1575
1583
1576 augroup HGCommand
1584 augroup HGCommand
@@ -1582,10 +1590,10 b' 5.2 HGCommand events\t\t\t\t *hgc'
1582 The following hooks are available:
1590 The following hooks are available:
1583
1591
1584 HGBufferCreated This event is fired just after a hg command result
1592 HGBufferCreated This event is fired just after a hg command result
1585 buffer is created and filled with the result of a hg
1593 buffer is created and filled with the result of a hg
1586 command. It is executed within the context of the HG
1594 command. It is executed within the context of the HG
1587 command buffer. The HGCommand buffer variables may be
1595 command buffer. The HGCommand buffer variables may be
1588 useful for handlers of this event (please see
1596 useful for handlers of this event (please see
1589 |hgcommand-buffer-variables|).
1597 |hgcommand-buffer-variables|).
1590
1598
1591 HGBufferSetup This event is fired just after HG buffer setup occurs,
1599 HGBufferSetup This event is fired just after HG buffer setup occurs,
@@ -1598,50 +1606,50 b' HGPluginFinish\t\tThis event is fired just'
1598 loads.
1606 loads.
1599
1607
1600 HGVimDiffFinish This event is fired just after the HGVimDiff command
1608 HGVimDiffFinish This event is fired just after the HGVimDiff command
1601 executes to allow customization of, for instance,
1609 executes to allow customization of, for instance,
1602 window placement and focus.
1610 window placement and focus.
1603
1611
1604 5.3 HGCommand buffer naming *hgcommand-naming*
1612 5.3 HGCommand buffer naming *hgcommand-naming*
1605
1613
1606 By default, the buffers containing the result of HG commands are nameless
1614 By default, the buffers containing the result of HG commands are nameless
1607 scratch buffers. It is intended that buffer variables of those buffers be
1615 scratch buffers. It is intended that buffer variables of those buffers be
1608 used to customize the statusline option so that the user may fully control
1616 used to customize the statusline option so that the user may fully control
1609 the display of result buffers.
1617 the display of result buffers.
1610
1618
1611 If the old-style naming is desired, please enable the
1619 If the old-style naming is desired, please enable the
1612 |HGCommandNameResultBuffers| variable. Then, each result buffer will
1620 |HGCommandNameResultBuffers| variable. Then, each result buffer will
1613 receive a unique name that includes the source file name, the HG command,
1621 receive a unique name that includes the source file name, the HG command,
1614 and any extra data (such as revision numbers) that were part of the
1622 and any extra data (such as revision numbers) that were part of the
1615 command.
1623 command.
1616
1624
1617 5.4 HGCommand status line support *hgcommand-statusline*
1625 5.4 HGCommand status line support *hgcommand-statusline*
1618
1626
1619 It is intended that the user will customize the |'statusline'| option to
1627 It is intended that the user will customize the |'statusline'| option to
1620 include HG result buffer attributes. A sample function that may be used in
1628 include HG result buffer attributes. A sample function that may be used in
1621 the |'statusline'| option is provided by the plugin, HGGetStatusLine(). In
1629 the |'statusline'| option is provided by the plugin, HGGetStatusLine(). In
1622 order to use that function in the status line, do something like the
1630 order to use that function in the status line, do something like the
1623 following: >
1631 following: >
1624
1632
1625 set statusline=%<%f\ %{HGGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P
1633 set statusline=%<%f\ %{HGGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P
1626 <
1634 <
1627 of which %{HGGetStatusLine()} is the relevant portion.
1635 of which %{HGGetStatusLine()} is the relevant portion.
1628
1636
1629 The sample HGGetStatusLine() function handles both HG result buffers and
1637 The sample HGGetStatusLine() function handles both HG result buffers and
1630 HG-managed files if HGCommand buffer management is enabled (please see
1638 HG-managed files if HGCommand buffer management is enabled (please see
1631 |hgcommand-buffer-management|).
1639 |hgcommand-buffer-management|).
1632
1640
1633 5.5 HGCommand buffer management *hgcommand-buffer-management*
1641 5.5 HGCommand buffer management *hgcommand-buffer-management*
1634
1642
1635 The HGCommand plugin can operate in buffer management mode, which means
1643 The HGCommand plugin can operate in buffer management mode, which means
1636 that it attempts to set two buffer variables ('HGRevision' and 'HGBranch')
1644 that it attempts to set two buffer variables ('HGRevision' and 'HGBranch')
1637 upon entry into a buffer. This is rather slow because it means that 'hg
1645 upon entry into a buffer. This is rather slow because it means that 'hg
1638 status' will be invoked at each entry into a buffer (during the |BufEnter|
1646 status' will be invoked at each entry into a buffer (during the |BufEnter|
1639 autocommand).
1647 autocommand).
1640
1648
1641 This mode is enablmed by default. In order to disable it, set the
1649 This mode is enabled by default. In order to disable it, set the
1642 |HGCommandEnableBufferSetup| variable to a false (zero) value. Enabling
1650 |HGCommandEnableBufferSetup| variable to a false (zero) value. Enabling
1643 this mode simply provides the buffer variables mentioned above. The user
1651 this mode simply provides the buffer variables mentioned above. The user
1644 must explicitly include those in the |'statusline'| option if they are to
1652 must explicitly include those in the |'statusline'| option if they are to
1645 appear in the status line (but see |hgcommand-statusline| for a simple way
1653 appear in the status line (but see |hgcommand-statusline| for a simple way
1646 to do that).
1654 to do that).
1647
1655
@@ -1655,10 +1663,10 b' 9.1 Split window annotation, by Michael '
1655 \:set nowrap<CR>
1663 \:set nowrap<CR>
1656 <
1664 <
1657
1665
1658 This splits the buffer vertically, puts an annotation on the left (minus
1666 This splits the buffer vertically, puts an annotation on the left (minus
1659 the header) with the width set to 40. An editable/normal copy is placed on
1667 the header) with the width set to 40. An editable/normal copy is placed on
1660 the right. The two versions are scroll locked so they move as one. and
1668 the right. The two versions are scroll locked so they move as one. and
1661 wrapping is turned off so that the lines line up correctly. The advantages
1669 wrapping is turned off so that the lines line up correctly. The advantages
1662 are...
1670 are...
1663
1671
1664 1) You get a versioning on the right.
1672 1) You get a versioning on the right.
@@ -1671,9 +1679,9 b' 8. Known bugs\t\t\t\t\t\t *hgcommand-bugs'
1671
1679
1672 Please let me know if you run across any.
1680 Please let me know if you run across any.
1673
1681
1674 HGVimDiff, when using the original (real) source buffer as one of the diff
1682 HGVimDiff, when using the original (real) source buffer as one of the diff
1675 buffers, uses some hacks to try to restore the state of the original buffer
1683 buffers, uses some hacks to try to restore the state of the original buffer
1676 when the scratch buffer containing the other version is destroyed. There
1684 when the scratch buffer containing the other version is destroyed. There
1677 may still be bugs in here, depending on many configuration details.
1685 may still be bugs in here, depending on many configuration details.
1678
1686
1679 ==============================================================================
1687 ==============================================================================
@@ -1686,4 +1694,4 b' 9. TODO \t\t\t\t\t\t *hgcommand-todo*'
1686 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
1694 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
1687 " v im:tw=78:ts=8:ft=help:norl:
1695 " v im:tw=78:ts=8:ft=help:norl:
1688 " vim600: set foldmethod=marker tabstop=8 shiftwidth=2 softtabstop=2 smartindent smarttab :
1696 " vim600: set foldmethod=marker tabstop=8 shiftwidth=2 softtabstop=2 smartindent smarttab :
1689 "fileencoding=iso-8859-15
1697 "fileencoding=iso-8859-15
@@ -14,7 +14,7 b''
14 </head>
14 </head>
15
15
16 <body>
16 <body>
17 <h1>Mercurial version 0.9 for Windows</h1>
17 <h1>Mercurial version 0.9.1 for Windows</h1>
18
18
19 <p>Welcome to Mercurial for Windows!</p>
19 <p>Welcome to Mercurial for Windows!</p>
20
20
@@ -4,7 +4,7 b''
4 [Setup]
4 [Setup]
5 AppCopyright=Copyright 2005, 2006 Matt Mackall and others
5 AppCopyright=Copyright 2005, 2006 Matt Mackall and others
6 AppName=Mercurial
6 AppName=Mercurial
7 AppVerName=Mercurial version 0.9
7 AppVerName=Mercurial version 0.9.1
8 InfoAfterFile=contrib/win32/postinstall.txt
8 InfoAfterFile=contrib/win32/postinstall.txt
9 LicenseFile=COPYING
9 LicenseFile=COPYING
10 ShowLanguageDialog=yes
10 ShowLanguageDialog=yes
@@ -14,10 +14,10 b' AppSupportURL=http://www.selenic.com/mer'
14 AppUpdatesURL=http://www.selenic.com/mercurial
14 AppUpdatesURL=http://www.selenic.com/mercurial
15 AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}
15 AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}
16 AppContact=mercurial@selenic.com
16 AppContact=mercurial@selenic.com
17 OutputBaseFilename=Mercurial-0.9
17 OutputBaseFilename=Mercurial-0.9.1
18 DefaultDirName={sd}\Mercurial
18 DefaultDirName={sd}\Mercurial
19 SourceDir=C:\hg\hg-release
19 SourceDir=C:\hg\hg-release
20 VersionInfoVersion=0.9
20 VersionInfoVersion=0.9.1
21 VersionInfoDescription=Mercurial distributed SCM
21 VersionInfoDescription=Mercurial distributed SCM
22 VersionInfoCopyright=Copyright 2005, 2006 Matt Mackall and others
22 VersionInfoCopyright=Copyright 2005, 2006 Matt Mackall and others
23 VersionInfoCompany=Matt Mackall and others
23 VersionInfoCompany=Matt Mackall and others
@@ -7,6 +7,62 b' file that comes with this package.'
7 Release Notes
7 Release Notes
8 -------------
8 -------------
9
9
10 2006-07-24 v0.9.1
11
12 Major changes between Mercurial 0.9 and 0.9.1:
13
14 New features:
15 - You can now configure your 'hgweb' server to let remote users
16 'push' changes over http.
17 - You can now 'import' a patch in a mail message by saving the mail
18 message, and importing it. This works for patches sent either
19 inline or as attachments.
20 - The 'diff' command now accepts '-rA:B' syntax as a synonym for
21 '-r A -r B', and adds '-b' and '-B' options.
22
23 New contributions and extensions:
24 - The 'acl' extension lets you lock down parts of a repository
25 against incoming changes
26 - The 'extdiff' extension lets you run your favourite graphical
27 change viewer
28 - Comprehensive integration with the 'vim' editor
29 - A restricted shell for 'ssh'-hosted repositories
30 - An importer for 'darcs' repositories
31
32 New hooks added:
33 - 'preupdate' is run before an update or merge in the working
34 directory.
35 - 'update' is run after an update or merge in the working
36 directory.
37
38 Behaviour changes:
39 - NOTE: Mercurial as installed by the Windows binary
40 installer no longer performs automatic line-ending conversion for
41 Unix/Linux compatibility. To re-enable this feature, edit your
42 'mercurial.ini' file after you upgrade.
43 - The Windows binary installer now automatically adds 'hg' to your
44 '%PATH%'.
45 - The 'backout' command now runs an editor by default, to let you
46 modify the commit message for a backed-out changeset.
47 - An earlier problem with parsing of tags has been fixed.
48 This makes tag parsing slower but more reliable.
49
50 Memory usage and performance improvements:
51 - The 'remove' command has been rewritten to be hundreds of times
52 faster in large repositories.
53 - It is now possible to 'clone' a repository very quickly over a
54 LAN, if the server is configured to allow it. See the new 'server'
55 section in the 'hgrc' documentation.
56
57 Other changes of note:
58 - Mercurial will now print help for an extension if you type 'hg
59 help EXT_NAME'.
60 - The usual array of bug fixes and documentation improvements.
61 - The integrated web server is now more WSGI-compliant.
62 - Work has begun to solidify Mercurial's API for use by third-party
63 packages.
64
65
10 2006-05-10 v0.9
66 2006-05-10 v0.9
11
67
12 * Major changes between Mercurial 0.8.1 and 0.9:
68 * Major changes between Mercurial 0.8.1 and 0.9:
@@ -216,6 +216,6 b' http://selenic.com/mailman/listinfo/merc'
216
216
217 COPYING
217 COPYING
218 -------
218 -------
219 Copyright \(C) 2005 Matt Mackall.
219 Copyright \(C) 2005, 2006 Matt Mackall.
220 Free use of this software is granted under the terms of the GNU General
220 Free use of this software is granted under the terms of the GNU General
221 Public License (GPL).
221 Public License (GPL).
@@ -30,6 +30,6 b' hg(1) - the command line interface to Me'
30
30
31 COPYING
31 COPYING
32 -------
32 -------
33 Copyright \(C) 2005 Matt Mackall.
33 Copyright \(C) 2005, 2006 Matt Mackall.
34 Free use of this software is granted under the terms of the GNU General
34 Free use of this software is granted under the terms of the GNU General
35 Public License (GPL).
35 Public License (GPL).
@@ -138,9 +138,17 b' email::'
138 from;;
138 from;;
139 Optional. Email address to use in "From" header and SMTP envelope
139 Optional. Email address to use in "From" header and SMTP envelope
140 of outgoing messages.
140 of outgoing messages.
141 to;;
142 Optional. Comma-separated list of recipients' email addresses.
143 cc;;
144 Optional. Comma-separated list of carbon copy recipients'
145 email addresses.
146 bcc;;
147 Optional. Comma-separated list of blind carbon copy
148 recipients' email addresses. Cannot be set interactively.
141 method;;
149 method;;
142 Optional. Method to use to send email messages. If value is
150 Optional. Method to use to send email messages. If value is
143 "smtp" (default), use SMTP (see section "[mail]" for
151 "smtp" (default), use SMTP (see section "[smtp]" for
144 configuration). Otherwise, use as name of program to run that
152 configuration). Otherwise, use as name of program to run that
145 acts like sendmail (takes "-f" option for sender, list of
153 acts like sendmail (takes "-f" option for sender, list of
146 recipients on command line, message on stdin). Normally, setting
154 recipients on command line, message on stdin). Normally, setting
@@ -194,7 +202,8 b' hooks::'
194
202
195 changegroup;;
203 changegroup;;
196 Run after a changegroup has been added via push, pull or
204 Run after a changegroup has been added via push, pull or
197 unbundle. ID of the first new changeset is in $HG_NODE.
205 unbundle. ID of the first new changeset is in $HG_NODE. URL from
206 which changes came is in $HG_URL.
198 commit;;
207 commit;;
199 Run after a changeset has been created in the local repository.
208 Run after a changeset has been created in the local repository.
200 ID of the newly created changeset is in $HG_NODE. Parent
209 ID of the newly created changeset is in $HG_NODE. Parent
@@ -202,7 +211,7 b' hooks::'
202 incoming;;
211 incoming;;
203 Run after a changeset has been pulled, pushed, or unbundled into
212 Run after a changeset has been pulled, pushed, or unbundled into
204 the local repository. The ID of the newly arrived changeset is in
213 the local repository. The ID of the newly arrived changeset is in
205 $HG_NODE.
214 $HG_NODE. URL that was source of changes came is in $HG_URL.
206 outgoing;;
215 outgoing;;
207 Run after sending changes from local repository to another. ID of
216 Run after sending changes from local repository to another. ID of
208 first changeset sent is in $HG_NODE. Source of operation is in
217 first changeset sent is in $HG_NODE. Source of operation is in
@@ -210,7 +219,8 b' hooks::'
210 prechangegroup;;
219 prechangegroup;;
211 Run before a changegroup is added via push, pull or unbundle.
220 Run before a changegroup is added via push, pull or unbundle.
212 Exit status 0 allows the changegroup to proceed. Non-zero status
221 Exit status 0 allows the changegroup to proceed. Non-zero status
213 will cause the push, pull or unbundle to fail.
222 will cause the push, pull or unbundle to fail. URL from which
223 changes will come is in $HG_URL.
214 precommit;;
224 precommit;;
215 Run before starting a local commit. Exit status 0 allows the
225 Run before starting a local commit. Exit status 0 allows the
216 commit to proceed. Non-zero status will cause the commit to fail.
226 commit to proceed. Non-zero status will cause the commit to fail.
@@ -236,7 +246,8 b' hooks::'
236 before accepting them. Passed the ID of the first new changeset
246 before accepting them. Passed the ID of the first new changeset
237 in $HG_NODE. Exit status 0 allows the transaction to commit.
247 in $HG_NODE. Exit status 0 allows the transaction to commit.
238 Non-zero status will cause the transaction to be rolled back and
248 Non-zero status will cause the transaction to be rolled back and
239 the push, pull or unbundle will fail.
249 the push, pull or unbundle will fail. URL that was source of
250 changes is in $HG_URL.
240 pretxncommit;;
251 pretxncommit;;
241 Run after a changeset has been created but the transaction not yet
252 Run after a changeset has been created but the transaction not yet
242 committed. Changeset is visible to hook program. This lets you
253 committed. Changeset is visible to hook program. This lets you
@@ -295,7 +306,7 b' http_proxy::'
295 smtp::
306 smtp::
296 Configuration for extensions that need to send email messages.
307 Configuration for extensions that need to send email messages.
297 host;;
308 host;;
298 Optional. Host name of mail server. Default: "mail".
309 Host name of mail server, e.g. "mail.example.com".
299 port;;
310 port;;
300 Optional. Port to connect to on mail server. Default: 25.
311 Optional. Port to connect to on mail server. Default: 25.
301 tls;;
312 tls;;
@@ -440,6 +451,9 b' web::'
440 push_ssl;;
451 push_ssl;;
441 Whether to require that inbound pushes be transported over SSL to
452 Whether to require that inbound pushes be transported over SSL to
442 prevent password sniffing. Default is true.
453 prevent password sniffing. Default is true.
454 stripes;;
455 How many lines a "zebra stripe" should span in multiline output.
456 Default is 1; set to 0 to disable.
443 style;;
457 style;;
444 Which template map style to use.
458 Which template map style to use.
445 templates;;
459 templates;;
@@ -862,6 +862,6 b' http://selenic.com/mailman/listinfo/mercurial[\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\xaa\xe3\x83\xb3\xe3\x82\xb0\xe3\x83\xaa\xe3\x82\xb9\xe3\x83\x88]'
862
862
863 著作権情報
863 著作権情報
864 -----
864 -----
865 Copyright (C) 2005 Matt Mackall.
865 Copyright (C) 2005, 2006 Matt Mackall.
866 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
866 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
867 認められます。
867 認められます。
@@ -32,6 +32,6 b' hg(1) - Mercurial \xe3\x82\xb7\xe3\x82\xb9\xe3\x83\x86\xe3\x83\xa0\xe3\x81\xb8\xe3\x81\xae\xe3\x82\xb3\xe3\x83\x9e\xe3\x83\xb3\xe3\x83\x89\xe3\x83\xa9\xe3\x82\xa4\xe3\x83\xb3\xe3\x82\xa4\xe3\x83\xb3\xe3\x82\xbf\xe3\x83\xbc\xe3\x83\x95\xe3\x82\xa7\xe3\x82\xa4\xe3\x82\xb9'
32
32
33 著作権情報
33 著作権情報
34 ----
34 ----
35 Copyright (C) 2005 Matt Mackall.
35 Copyright (C) 2005, 2006 Matt Mackall.
36 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
36 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
37 認められます。
37 認められます。
@@ -5,34 +5,49 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 # allow to use external programs to compare revisions, or revision
8 # The `extdiff' Mercurial extension allows you to use external programs
9 # with working dir. program is called with two arguments: paths to
9 # to compare revisions, or revision with working dir. The external diff
10 # directories containing snapshots of files to compare.
10 # programs are called with a configurable set of options and two
11 # non-option arguments: paths to directories containing snapshots of
12 # files to compare.
11 #
13 #
12 # to enable:
14 # To enable this extension:
13 #
15 #
14 # [extensions]
16 # [extensions]
15 # hgext.extdiff =
17 # hgext.extdiff =
16 #
18 #
17 # also allows to configure new diff commands, so you do not need to
19 # The `extdiff' extension also allows to configure new diff commands, so
18 # type "hg extdiff -p kdiff3" always.
20 # you do not need to type "hg extdiff -p kdiff3" always.
19 #
21 #
20 # [extdiff]
22 # [extdiff]
23 # # add new command that runs GNU diff(1) in 'context diff' mode
24 # cmd.cdiff = gdiff
25 # opts.cdiff = -Nprc5
21 # # add new command called vdiff, runs kdiff3
26 # # add new command called vdiff, runs kdiff3
22 # cmd.vdiff = kdiff3
27 # cmd.vdiff = kdiff3
23 # # add new command called meld, runs meld (no need to name twice)
28 # # add new command called meld, runs meld (no need to name twice)
24 # cmd.meld =
29 # cmd.meld =
30 # # add new command called vimdiff, runs gvimdiff with DirDiff plugin
31 # #(see http://www.vim.org/scripts/script.php?script_id=102)
32 # cmd.vimdiff = LC_ALL=C gvim -f '+bdel 1 2' '+ execute "DirDiff ".argv(0)." ".argv(1)'
25 #
33 #
26 # you can use -I/-X and list of file or directory names like normal
34 # Each custom diff commands can have two parts: a `cmd' and an `opts'
27 # "hg diff" command. extdiff makes snapshots of only needed files, so
35 # part. The cmd.xxx option defines the name of an executable program
28 # compare program will be fast.
36 # that will be run, and opts.xxx defines a set of command-line options
37 # which will be inserted to the command between the program name and
38 # the files/directories to diff (i.e. the cdiff example above).
39 #
40 # You can use -I/-X and list of file or directory names like normal
41 # "hg diff" command. The `extdiff' extension makes snapshots of only
42 # needed files, so running the external diff program will actually be
43 # pretty fast (at least faster than having to compare the entire tree).
29
44
30 from mercurial.demandload import demandload
45 from mercurial.demandload import demandload
31 from mercurial.i18n import gettext as _
46 from mercurial.i18n import gettext as _
32 from mercurial.node import *
47 from mercurial.node import *
33 demandload(globals(), 'mercurial:commands,util os shutil tempfile')
48 demandload(globals(), 'mercurial:commands,cmdutil,util os shutil tempfile')
34
49
35 def dodiff(ui, repo, diffcmd, pats, opts):
50 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
36 def snapshot_node(files, node):
51 def snapshot_node(files, node):
37 '''snapshot files as of some revision'''
52 '''snapshot files as of some revision'''
38 changes = repo.changelog.read(node)
53 changes = repo.changelog.read(node)
@@ -76,9 +91,9 b' def dodiff(ui, repo, diffcmd, pats, opts'
76 return dirname
91 return dirname
77
92
78 node1, node2 = commands.revpair(ui, repo, opts['rev'])
93 node1, node2 = commands.revpair(ui, repo, opts['rev'])
79 files, matchfn, anypats = commands.matchpats(repo, pats, opts)
94 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
80 modified, added, removed, deleted, unknown = repo.changes(
95 modified, added, removed, deleted, unknown = repo.status(
81 node1, node2, files, match=matchfn)
96 node1, node2, files, match=matchfn)[:5]
82 if not (modified or added or removed):
97 if not (modified or added or removed):
83 return 0
98 return 0
84
99
@@ -89,9 +104,12 b' def dodiff(ui, repo, diffcmd, pats, opts'
89 dir2 = snapshot_node(modified + added, node2)
104 dir2 = snapshot_node(modified + added, node2)
90 else:
105 else:
91 dir2 = snapshot_wdir(modified + added)
106 dir2 = snapshot_wdir(modified + added)
92 util.system('%s %s "%s" "%s"' %
107 cmdline = ('%s %s %s %s' %
93 (diffcmd, ' '.join(opts['option']), dir1, dir2),
108 (util.shellquote(diffcmd),
94 cwd=tmproot)
109 ' '.join(map(util.shellquote, diffopts)),
110 util.shellquote(dir1), util.shellquote(dir2)))
111 ui.debug('running %r in %s\n' % (cmdline, tmproot))
112 util.system(cmdline, cwd=tmproot)
95 return 1
113 return 1
96 finally:
114 finally:
97 ui.note(_('cleaning up temp directory\n'))
115 ui.note(_('cleaning up temp directory\n'))
@@ -101,7 +119,9 b' def extdiff(ui, repo, *pats, **opts):'
101 '''use external program to diff repository (or selected files)
119 '''use external program to diff repository (or selected files)
102
120
103 Show differences between revisions for the specified files, using
121 Show differences between revisions for the specified files, using
104 an external program. The default program used is "diff -Npru".
122 an external program. The default program used is diff, with
123 default options "-Npru".
124
105 To select a different program, use the -p option. The program
125 To select a different program, use the -p option. The program
106 will be passed the names of two directories to compare. To pass
126 will be passed the names of two directories to compare. To pass
107 additional options to the program, use the -o option. These will
127 additional options to the program, use the -o option. These will
@@ -112,7 +132,8 b' def extdiff(ui, repo, *pats, **opts):'
112 specified then that revision is compared to the working
132 specified then that revision is compared to the working
113 directory, and, when no revisions are specified, the
133 directory, and, when no revisions are specified, the
114 working directory files are compared to its parent.'''
134 working directory files are compared to its parent.'''
115 return dodiff(ui, repo, opts['program'] or 'diff -Npru', pats, opts)
135 return dodiff(ui, repo, opts['program'] or 'diff',
136 opts['option'] or ['-Npru'], pats, opts)
116
137
117 cmdtable = {
138 cmdtable = {
118 "extdiff":
139 "extdiff":
@@ -130,20 +151,24 b' def uisetup(ui):'
130 if not cmd.startswith('cmd.'): continue
151 if not cmd.startswith('cmd.'): continue
131 cmd = cmd[4:]
152 cmd = cmd[4:]
132 if not path: path = cmd
153 if not path: path = cmd
154 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
155 diffopts = diffopts and [diffopts] or []
133 def save(cmd, path):
156 def save(cmd, path):
134 '''use closure to save diff command to use'''
157 '''use closure to save diff command to use'''
135 def mydiff(ui, repo, *pats, **opts):
158 def mydiff(ui, repo, *pats, **opts):
136 return dodiff(ui, repo, path, pats, opts)
159 return dodiff(ui, repo, path, diffopts, pats, opts)
137 mydiff.__doc__ = '''use %s to diff repository (or selected files)
160 mydiff.__doc__ = '''use %(path)r to diff repository (or selected files)
138
161
139 Show differences between revisions for the specified
162 Show differences between revisions for the specified
140 files, using the %s program.
163 files, using the %(path)r program.
141
164
142 When two revision arguments are given, then changes are
165 When two revision arguments are given, then changes are
143 shown between those revisions. If only one revision is
166 shown between those revisions. If only one revision is
144 specified then that revision is compared to the working
167 specified then that revision is compared to the working
145 directory, and, when no revisions are specified, the
168 directory, and, when no revisions are specified, the
146 working directory files are compared to its parent.''' % (cmd, cmd)
169 working directory files are compared to its parent.''' % {
170 'path': path,
171 }
147 return mydiff
172 return mydiff
148 cmdtable[cmd] = (save(cmd, path),
173 cmdtable[cmd] = (save(cmd, path),
149 cmdtable['extdiff'][1][1:],
174 cmdtable['extdiff'][1][1:],
@@ -221,7 +221,7 b' def sign(ui, repo, *revs, **opts):'
221 repo.opener("localsigs", "ab").write(sigmessage)
221 repo.opener("localsigs", "ab").write(sigmessage)
222 return
222 return
223
223
224 for x in repo.changes():
224 for x in repo.status()[:5]:
225 if ".hgsigs" in x and not opts["force"]:
225 if ".hgsigs" in x and not opts["force"]:
226 raise util.Abort(_("working copy of .hgsigs is changed "
226 raise util.Abort(_("working copy of .hgsigs is changed "
227 "(please commit .hgsigs manually "
227 "(please commit .hgsigs manually "
@@ -23,10 +23,10 b' def lookup_rev(ui, repo, rev=None):'
23 return parents.pop()
23 return parents.pop()
24
24
25 def check_clean(ui, repo):
25 def check_clean(ui, repo):
26 modified, added, removed, deleted, unknown = repo.changes()
26 modified, added, removed, deleted, unknown = repo.status()[:5]
27 if modified or added or removed:
27 if modified or added or removed:
28 ui.warn("Repository is not clean, please commit or revert\n")
28 ui.warn("Repository is not clean, please commit or revert\n")
29 sys.exit(1)
29 sys.exit(1)
30
30
31 class bisect(object):
31 class bisect(object):
32 """dichotomic search in the DAG of changesets"""
32 """dichotomic search in the DAG of changesets"""
@@ -50,7 +50,7 b' class bisect(object):'
50 if r:
50 if r:
51 self.badrev = hg.bin(r.pop(0))
51 self.badrev = hg.bin(r.pop(0))
52
52
53 def __del__(self):
53 def write(self):
54 if not os.path.isdir(self.path):
54 if not os.path.isdir(self.path):
55 return
55 return
56 f = self.opener(self.good_path, "w")
56 f = self.opener(self.good_path, "w")
@@ -197,7 +197,7 b' class bisect(object):'
197 check_clean(self.ui, self.repo)
197 check_clean(self.ui, self.repo)
198 rev = self.next()
198 rev = self.next()
199 if rev is not None:
199 if rev is not None:
200 return self.repo.update(rev, force=True)
200 return hg.clean(self.repo, rev)
201
201
202 def good(self, rev):
202 def good(self, rev):
203 self.goodrevs.append(rev)
203 self.goodrevs.append(rev)
@@ -288,7 +288,10 b' for subcommands see "hg bisect help\\"'
288 if len(args) > bisectcmdtable[cmd][1]:
288 if len(args) > bisectcmdtable[cmd][1]:
289 ui.warn(_("bisect: Too many arguments\n"))
289 ui.warn(_("bisect: Too many arguments\n"))
290 return help_()
290 return help_()
291 return bisectcmdtable[cmd][0](*args)
291 try:
292 return bisectcmdtable[cmd][0](*args)
293 finally:
294 b.write()
292
295
293 cmdtable = {
296 cmdtable = {
294 "bisect": (bisect_run, [], _("hg bisect [help|init|reset|next|good|bad]")),
297 "bisect": (bisect_run, [], _("hg bisect [help|init|reset|next|good|bad]")),
@@ -1,12 +1,13 b''
1 # Minimal support for git commands on an hg repository
1 # Minimal support for git commands on an hg repository
2 #
2 #
3 # Copyright 2005 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 import time, sys, signal, os
8 from mercurial.demandload import *
9 from mercurial import hg, mdiff, fancyopts, commands, ui, util
9 demandload(globals(), 'time sys signal os')
10 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util')
10
11
11 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
12 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
12 changes=None, text=False):
13 changes=None, text=False):
@@ -14,7 +15,7 b' def dodiff(fp, ui, repo, node1, node2, f'
14 return time.asctime(time.gmtime(c[2][0]))
15 return time.asctime(time.gmtime(c[2][0]))
15
16
16 if not changes:
17 if not changes:
17 changes = repo.changes(node1, node2, files, match=match)
18 changes = repo.status(node1, node2, files, match=match)[:5]
18 modified, added, removed, deleted, unknown = changes
19 modified, added, removed, deleted, unknown = changes
19 if files:
20 if files:
20 modified, added, removed = map(lambda x: filterfiles(files, x),
21 modified, added, removed = map(lambda x: filterfiles(files, x),
@@ -67,12 +68,12 b' def difftree(ui, repo, node1=None, node2'
67 if node2:
68 if node2:
68 change = repo.changelog.read(node2)
69 change = repo.changelog.read(node2)
69 mmap2 = repo.manifest.read(change[0])
70 mmap2 = repo.manifest.read(change[0])
70 modified, added, removed, deleted, unknown = repo.changes(node1, node2)
71 modified, added, removed, deleted, unknown = repo.status(node1, node2)[:5]
71 def read(f): return repo.file(f).read(mmap2[f])
72 def read(f): return repo.file(f).read(mmap2[f])
72 date2 = date(change)
73 date2 = date(change)
73 else:
74 else:
74 date2 = time.asctime()
75 date2 = time.asctime()
75 modified, added, removed, deleted, unknown = repo.changes(node1)
76 modified, added, removed, deleted, unknown = repo.status(node1)[:5]
76 if not node1:
77 if not node1:
77 node1 = repo.dirstate.parents()[0]
78 node1 = repo.dirstate.parents()[0]
78 def read(f): return file(os.path.join(repo.root, f)).read()
79 def read(f): return file(os.path.join(repo.root, f)).read()
@@ -334,6 +335,3 b' cmdtable = {'
334 ('n', 'max-count', 0, 'max-count')],
335 ('n', 'max-count', 0, 'max-count')],
335 "hg debug-rev-list [options] revs"),
336 "hg debug-rev-list [options] revs"),
336 }
337 }
337
338 def reposetup(ui, repo):
339 pass
This diff has been collapsed as it changes many lines, (1338 lines changed) Show them Hide them
@@ -1,6 +1,6 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -30,22 +30,30 b' refresh contents of top applied patch '
30 '''
30 '''
31
31
32 from mercurial.demandload import *
32 from mercurial.demandload import *
33 from mercurial.i18n import gettext as _
33 demandload(globals(), "os sys re struct traceback errno bz2")
34 demandload(globals(), "os sys re struct traceback errno bz2")
34 from mercurial.i18n import gettext as _
35 demandload(globals(), "mercurial:cmdutil,commands,hg,patch,revlog,ui,util")
35 from mercurial import ui, hg, revlog, commands, util
36
37 commands.norepo += " qclone qversion"
36
38
37 versionstr = "0.45"
39 class statusentry:
40 def __init__(self, rev, name=None):
41 if not name:
42 fields = rev.split(':')
43 if len(fields) == 2:
44 self.rev, self.name = fields
45 else:
46 self.rev, self.name = None, None
47 else:
48 self.rev, self.name = rev, name
38
49
39 repomap = {}
50 def __str__(self):
51 return self.rev + ':' + self.name
40
52
41 commands.norepo += " qversion"
42 class queue:
53 class queue:
43 def __init__(self, ui, path, patchdir=None):
54 def __init__(self, ui, path, patchdir=None):
44 self.basepath = path
55 self.basepath = path
45 if patchdir:
56 self.path = patchdir or os.path.join(path, "patches")
46 self.path = patchdir
47 else:
48 self.path = os.path.join(path, "patches")
49 self.opener = util.opener(self.path)
57 self.opener = util.opener(self.path)
50 self.ui = ui
58 self.ui = ui
51 self.applied = []
59 self.applied = []
@@ -54,13 +62,26 b' class queue:'
54 self.series_dirty = 0
62 self.series_dirty = 0
55 self.series_path = "series"
63 self.series_path = "series"
56 self.status_path = "status"
64 self.status_path = "status"
65 self.guards_path = "guards"
66 self.active_guards = None
67 self.guards_dirty = False
68 self._diffopts = None
57
69
58 if os.path.exists(os.path.join(self.path, self.series_path)):
70 if os.path.exists(self.join(self.series_path)):
59 self.full_series = self.opener(self.series_path).read().splitlines()
71 self.full_series = self.opener(self.series_path).read().splitlines()
60 self.read_series(self.full_series)
72 self.parse_series()
73
74 if os.path.exists(self.join(self.status_path)):
75 lines = self.opener(self.status_path).read().splitlines()
76 self.applied = [statusentry(l) for l in lines]
61
77
62 if os.path.exists(os.path.join(self.path, self.status_path)):
78 def diffopts(self):
63 self.applied = self.opener(self.status_path).read().splitlines()
79 if self._diffopts is None:
80 self._diffopts = patch.diffopts(self.ui)
81 return self._diffopts
82
83 def join(self, *p):
84 return os.path.join(self.path, *p)
64
85
65 def find_series(self, patch):
86 def find_series(self, patch):
66 pre = re.compile("(\s*)([^#]+)")
87 pre = re.compile("(\s*)([^#]+)")
@@ -75,34 +96,132 b' class queue:'
75 index += 1
96 index += 1
76 return None
97 return None
77
98
78 def read_series(self, list):
99 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
79 def matcher(list):
100
80 pre = re.compile("(\s*)([^#]+)")
101 def parse_series(self):
81 for l in list:
82 m = pre.match(l)
83 if m:
84 s = m.group(2)
85 s = s.rstrip()
86 if len(s) > 0:
87 yield s
88 self.series = []
102 self.series = []
89 self.series = [ x for x in matcher(list) ]
103 self.series_guards = []
104 for l in self.full_series:
105 h = l.find('#')
106 if h == -1:
107 patch = l
108 comment = ''
109 elif h == 0:
110 continue
111 else:
112 patch = l[:h]
113 comment = l[h:]
114 patch = patch.strip()
115 if patch:
116 self.series.append(patch)
117 self.series_guards.append(self.guard_re.findall(comment))
118
119 def check_guard(self, guard):
120 bad_chars = '# \t\r\n\f'
121 first = guard[0]
122 for c in '-+':
123 if first == c:
124 return (_('guard %r starts with invalid character: %r') %
125 (guard, c))
126 for c in bad_chars:
127 if c in guard:
128 return _('invalid character in guard %r: %r') % (guard, c)
129
130 def set_active(self, guards):
131 for guard in guards:
132 bad = self.check_guard(guard)
133 if bad:
134 raise util.Abort(bad)
135 guards = dict.fromkeys(guards).keys()
136 guards.sort()
137 self.ui.debug('active guards: %s\n' % ' '.join(guards))
138 self.active_guards = guards
139 self.guards_dirty = True
140
141 def active(self):
142 if self.active_guards is None:
143 self.active_guards = []
144 try:
145 guards = self.opener(self.guards_path).read().split()
146 except IOError, err:
147 if err.errno != errno.ENOENT: raise
148 guards = []
149 for i, guard in enumerate(guards):
150 bad = self.check_guard(guard)
151 if bad:
152 self.ui.warn('%s:%d: %s\n' %
153 (self.join(self.guards_path), i + 1, bad))
154 else:
155 self.active_guards.append(guard)
156 return self.active_guards
157
158 def set_guards(self, idx, guards):
159 for g in guards:
160 if len(g) < 2:
161 raise util.Abort(_('guard %r too short') % g)
162 if g[0] not in '-+':
163 raise util.Abort(_('guard %r starts with invalid char') % g)
164 bad = self.check_guard(g[1:])
165 if bad:
166 raise util.Abort(bad)
167 drop = self.guard_re.sub('', self.full_series[idx])
168 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
169 self.parse_series()
170 self.series_dirty = True
171
172 def pushable(self, idx):
173 if isinstance(idx, str):
174 idx = self.series.index(idx)
175 patchguards = self.series_guards[idx]
176 if not patchguards:
177 return True, None
178 default = False
179 guards = self.active()
180 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
181 if exactneg:
182 return False, exactneg[0]
183 pos = [g for g in patchguards if g[0] == '+']
184 exactpos = [g for g in pos if g[1:] in guards]
185 if pos:
186 if exactpos:
187 return True, exactpos[0]
188 return False, pos
189 return True, ''
190
191 def explain_pushable(self, idx, all_patches=False):
192 write = all_patches and self.ui.write or self.ui.warn
193 if all_patches or self.ui.verbose:
194 if isinstance(idx, str):
195 idx = self.series.index(idx)
196 pushable, why = self.pushable(idx)
197 if all_patches and pushable:
198 if why is None:
199 write(_('allowing %s - no guards in effect\n') %
200 self.series[idx])
201 else:
202 if not why:
203 write(_('allowing %s - no matching negative guards\n') %
204 self.series[idx])
205 else:
206 write(_('allowing %s - guarded by %r\n') %
207 (self.series[idx], why))
208 if not pushable:
209 if why:
210 write(_('skipping %s - guarded by %r\n') %
211 (self.series[idx], ' '.join(why)))
212 else:
213 write(_('skipping %s - no matching guards\n') %
214 self.series[idx])
90
215
91 def save_dirty(self):
216 def save_dirty(self):
92 if self.applied_dirty:
217 def write_list(items, path):
93 if len(self.applied) > 0:
218 fp = self.opener(path, 'w')
94 nl = "\n"
219 for i in items:
95 else:
220 print >> fp, i
96 nl = ""
221 fp.close()
97 f = self.opener(self.status_path, "w")
222 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
98 f.write("\n".join(self.applied) + nl)
223 if self.series_dirty: write_list(self.full_series, self.series_path)
99 if self.series_dirty:
224 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
100 if len(self.full_series) > 0:
101 nl = "\n"
102 else:
103 nl = ""
104 f = self.opener(self.series_path, "w")
105 f.write("\n".join(self.full_series) + nl)
106
225
107 def readheaders(self, patch):
226 def readheaders(self, patch):
108 def eatdiff(lines):
227 def eatdiff(lines):
@@ -122,7 +241,7 b' class queue:'
122 else:
241 else:
123 break
242 break
124
243
125 pf = os.path.join(self.path, patch)
244 pf = self.join(patch)
126 message = []
245 message = []
127 comments = []
246 comments = []
128 user = None
247 user = None
@@ -133,6 +252,9 b' class queue:'
133
252
134 for line in file(pf):
253 for line in file(pf):
135 line = line.rstrip()
254 line = line.rstrip()
255 if line.startswith('diff --git'):
256 diffstart = 2
257 break
136 if diffstart:
258 if diffstart:
137 if line.startswith('+++ '):
259 if line.startswith('+++ '):
138 diffstart = 2
260 diffstart = 2
@@ -178,6 +300,13 b' class queue:'
178 message.insert(0, subject)
300 message.insert(0, subject)
179 return (message, comments, user, date, diffstart > 1)
301 return (message, comments, user, date, diffstart > 1)
180
302
303 def printdiff(self, repo, node1, node2=None, files=None,
304 fp=None, changes=None, opts={}):
305 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
306
307 patch.diff(repo, node1, node2, fns, match=matchfn,
308 fp=fp, changes=changes, opts=self.diffopts())
309
181 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
310 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
182 # first try just applying the patch
311 # first try just applying the patch
183 (err, n) = self.apply(repo, [ patch ], update_status=False,
312 (err, n) = self.apply(repo, [ patch ], update_status=False,
@@ -187,35 +316,31 b' class queue:'
187 return (err, n)
316 return (err, n)
188
317
189 if n is None:
318 if n is None:
190 self.ui.warn("apply failed for patch %s\n" % patch)
319 raise util.Abort(_("apply failed for patch %s") % patch)
191 sys.exit(1)
192
320
193 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)
194
322
195 # apply failed, strip away that rev and merge.
323 # apply failed, strip away that rev and merge.
196 repo.update(head, allow=False, force=True, wlock=wlock)
324 hg.clean(repo, head, wlock=wlock)
197 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
325 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
198
326
199 c = repo.changelog.read(rev)
327 c = repo.changelog.read(rev)
200 ret = repo.update(rev, allow=True, wlock=wlock)
328 ret = hg.merge(repo, rev, wlock=wlock)
201 if ret:
329 if ret:
202 self.ui.warn("update returned %d\n" % ret)
330 raise util.Abort(_("update returned %d") % ret)
203 sys.exit(1)
204 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)
205 if n == None:
332 if n == None:
206 self.ui.warn("repo commit failed\n")
333 raise util.Abort(_("repo commit failed"))
207 sys.exit(1)
208 try:
334 try:
209 message, comments, user, date, patchfound = mergeq.readheaders(patch)
335 message, comments, user, date, patchfound = mergeq.readheaders(patch)
210 except:
336 except:
211 self.ui.warn("Unable to read %s\n" % patch)
337 raise util.Abort(_("unable to read %s") % patch)
212 sys.exit(1)
213
338
214 patchf = self.opener(patch, "w")
339 patchf = self.opener(patch, "w")
215 if comments:
340 if comments:
216 comments = "\n".join(comments) + '\n\n'
341 comments = "\n".join(comments) + '\n\n'
217 patchf.write(comments)
342 patchf.write(comments)
218 commands.dodiff(patchf, self.ui, repo, head, n)
343 self.printdiff(repo, head, n, fp=patchf)
219 patchf.close()
344 patchf.close()
220 return (0, n)
345 return (0, n)
221
346
@@ -226,12 +351,10 b' class queue:'
226 return p1
351 return p1
227 if len(self.applied) == 0:
352 if len(self.applied) == 0:
228 return None
353 return None
229 (top, patch) = self.applied[-1].split(':')
354 return revlog.bin(self.applied[-1].rev)
230 top = revlog.bin(top)
231 return top
232 pp = repo.changelog.parents(rev)
355 pp = repo.changelog.parents(rev)
233 if pp[1] != revlog.nullid:
356 if pp[1] != revlog.nullid:
234 arevs = [ x.split(':')[0] for x in self.applied ]
357 arevs = [ x.rev for x in self.applied ]
235 p0 = revlog.hex(pp[0])
358 p0 = revlog.hex(pp[0])
236 p1 = revlog.hex(pp[1])
359 p1 = revlog.hex(pp[1])
237 if p0 in arevs:
360 if p0 in arevs:
@@ -251,17 +374,20 b' class queue:'
251 pname = ".hg.patches.merge.marker"
374 pname = ".hg.patches.merge.marker"
252 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
375 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
253 wlock=wlock)
376 wlock=wlock)
254 self.applied.append(revlog.hex(n) + ":" + pname)
377 self.applied.append(statusentry(revlog.hex(n), pname))
255 self.applied_dirty = 1
378 self.applied_dirty = 1
256
379
257 head = self.qparents(repo)
380 head = self.qparents(repo)
258
381
259 for patch in series:
382 for patch in series:
260 patch = mergeq.lookup(patch)
383 patch = mergeq.lookup(patch, strict=True)
261 if not patch:
384 if not patch:
262 self.ui.warn("patch %s does not exist\n" % patch)
385 self.ui.warn("patch %s does not exist\n" % patch)
263 return (1, None)
386 return (1, None)
264
387 pushable, reason = self.pushable(patch)
388 if not pushable:
389 self.explain_pushable(patch, all_patches=True)
390 continue
265 info = mergeq.isapplied(patch)
391 info = mergeq.isapplied(patch)
266 if not info:
392 if not info:
267 self.ui.warn("patch %s is not applied\n" % patch)
393 self.ui.warn("patch %s is not applied\n" % patch)
@@ -269,102 +395,80 b' class queue:'
269 rev = revlog.bin(info[1])
395 rev = revlog.bin(info[1])
270 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
396 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
271 if head:
397 if head:
272 self.applied.append(revlog.hex(head) + ":" + patch)
398 self.applied.append(statusentry(revlog.hex(head), patch))
273 self.applied_dirty = 1
399 self.applied_dirty = 1
274 if err:
400 if err:
275 return (err, head)
401 return (err, head)
276 return (0, head)
402 return (0, head)
277
403
404 def patch(self, repo, patchfile):
405 '''Apply patchfile to the working directory.
406 patchfile: file name of patch'''
407 try:
408 (files, fuzz) = patch.patch(patchfile, self.ui, strip=1,
409 cwd=repo.root)
410 except Exception, inst:
411 self.ui.note(str(inst) + '\n')
412 if not self.ui.verbose:
413 self.ui.warn("patch failed, unable to continue (try -v)\n")
414 return (False, [], False)
415
416 return (True, files, fuzz)
417
278 def apply(self, repo, series, list=False, update_status=True,
418 def apply(self, repo, series, list=False, update_status=True,
279 strict=False, patchdir=None, merge=None, wlock=None):
419 strict=False, patchdir=None, merge=None, wlock=None):
280 # TODO unify with commands.py
420 # TODO unify with commands.py
281 if not patchdir:
421 if not patchdir:
282 patchdir = self.path
422 patchdir = self.path
283 pwd = os.getcwd()
284 os.chdir(repo.root)
285 err = 0
423 err = 0
286 if not wlock:
424 if not wlock:
287 wlock = repo.wlock()
425 wlock = repo.wlock()
288 lock = repo.lock()
426 lock = repo.lock()
289 tr = repo.transaction()
427 tr = repo.transaction()
290 n = None
428 n = None
291 for patch in series:
429 for patchname in series:
292 self.ui.warn("applying %s\n" % patch)
430 pushable, reason = self.pushable(patchname)
293 pf = os.path.join(patchdir, patch)
431 if not pushable:
432 self.explain_pushable(patchname, all_patches=True)
433 continue
434 self.ui.warn("applying %s\n" % patchname)
435 pf = os.path.join(patchdir, patchname)
294
436
295 try:
437 try:
296 message, comments, user, date, patchfound = self.readheaders(patch)
438 message, comments, user, date, patchfound = self.readheaders(patchname)
297 except:
439 except:
298 self.ui.warn("Unable to read %s\n" % pf)
440 self.ui.warn("Unable to read %s\n" % patchname)
299 err = 1
441 err = 1
300 break
442 break
301
443
302 if not message:
444 if not message:
303 message = "imported patch %s\n" % patch
445 message = "imported patch %s\n" % patchname
304 else:
446 else:
305 if list:
447 if list:
306 message.append("\nimported patch %s" % patch)
448 message.append("\nimported patch %s" % patchname)
307 message = '\n'.join(message)
449 message = '\n'.join(message)
308
450
309 try:
451 (patcherr, files, fuzz) = self.patch(repo, pf)
310 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
452 patcherr = not patcherr
311 f = os.popen("%s -p1 --no-backup-if-mismatch < '%s'" % (pp, pf))
312 except:
313 self.ui.warn("patch failed, unable to continue (try -v)\n")
314 err = 1
315 break
316 files = []
317 fuzz = False
318 for l in f:
319 l = l.rstrip('\r\n');
320 if self.ui.verbose:
321 self.ui.warn(l + "\n")
322 if l[:14] == 'patching file ':
323 pf = os.path.normpath(l[14:])
324 # when patch finds a space in the file name, it puts
325 # single quotes around the filename. strip them off
326 if pf[0] == "'" and pf[-1] == "'":
327 pf = pf[1:-1]
328 if pf not in files:
329 files.append(pf)
330 printed_file = False
331 file_str = l
332 elif l.find('with fuzz') >= 0:
333 if not printed_file:
334 self.ui.warn(file_str + '\n')
335 printed_file = True
336 self.ui.warn(l + '\n')
337 fuzz = True
338 elif l.find('saving rejects to file') >= 0:
339 self.ui.warn(l + '\n')
340 elif l.find('FAILED') >= 0:
341 if not printed_file:
342 self.ui.warn(file_str + '\n')
343 printed_file = True
344 self.ui.warn(l + '\n')
345 patcherr = f.close()
346
453
347 if merge and len(files) > 0:
454 if merge and files:
348 # Mark as merged and update dirstate parent info
455 # Mark as merged and update dirstate parent info
349 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
456 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
350 p1, p2 = repo.dirstate.parents()
457 p1, p2 = repo.dirstate.parents()
351 repo.dirstate.setparents(p1, merge)
458 repo.dirstate.setparents(p1, merge)
352 if len(files) > 0:
459 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
353 commands.addremove_lock(self.ui, repo, files,
354 opts={}, wlock=wlock)
355 n = repo.commit(files, message, user, date, force=1, lock=lock,
460 n = repo.commit(files, message, user, date, force=1, lock=lock,
356 wlock=wlock)
461 wlock=wlock)
357
462
358 if n == None:
463 if n == None:
359 self.ui.warn("repo commit failed\n")
464 raise util.Abort(_("repo commit failed"))
360 sys.exit(1)
361
465
362 if update_status:
466 if update_status:
363 self.applied.append(revlog.hex(n) + ":" + patch)
467 self.applied.append(statusentry(revlog.hex(n), patchname))
364
468
365 if patcherr:
469 if patcherr:
366 if not patchfound:
470 if not patchfound:
367 self.ui.warn("patch %s is empty\n" % patch)
471 self.ui.warn("patch %s is empty\n" % patchname)
368 err = 0
472 err = 0
369 else:
473 else:
370 self.ui.warn("patch failed, rejects left in working dir\n")
474 self.ui.warn("patch failed, rejects left in working dir\n")
@@ -376,49 +480,58 b' class queue:'
376 err = 1
480 err = 1
377 break
481 break
378 tr.close()
482 tr.close()
379 os.chdir(pwd)
380 return (err, n)
483 return (err, n)
381
484
382 def delete(self, repo, patch):
485 def delete(self, repo, patches, keep=False):
383 patch = self.lookup(patch)
486 realpatches = []
384 info = self.isapplied(patch)
487 for patch in patches:
385 if info:
488 patch = self.lookup(patch, strict=True)
386 self.ui.warn("cannot delete applied patch %s\n" % patch)
489 info = self.isapplied(patch)
387 sys.exit(1)
490 if info:
388 if patch not in self.series:
491 raise util.Abort(_("cannot delete applied patch %s") % patch)
389 self.ui.warn("patch %s not in series file\n" % patch)
492 if patch not in self.series:
390 sys.exit(1)
493 raise util.Abort(_("patch %s not in series file") % patch)
391 i = self.find_series(patch)
494 realpatches.append(patch)
392 del self.full_series[i]
495
393 self.read_series(self.full_series)
496 if not keep:
497 r = self.qrepo()
498 if r:
499 r.remove(realpatches, True)
500 else:
501 os.unlink(self.join(patch))
502
503 indices = [self.find_series(p) for p in realpatches]
504 indices.sort()
505 for i in indices[-1::-1]:
506 del self.full_series[i]
507 self.parse_series()
394 self.series_dirty = 1
508 self.series_dirty = 1
395
509
396 def check_toppatch(self, repo):
510 def check_toppatch(self, repo):
397 if len(self.applied) > 0:
511 if len(self.applied) > 0:
398 (top, patch) = self.applied[-1].split(':')
512 top = revlog.bin(self.applied[-1].rev)
399 top = revlog.bin(top)
400 pp = repo.dirstate.parents()
513 pp = repo.dirstate.parents()
401 if top not in pp:
514 if top not in pp:
402 self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
515 raise util.Abort(_("queue top not at same revision as working directory"))
403 sys.exit(1)
404 return top
516 return top
405 return None
517 return None
406 def check_localchanges(self, repo):
518 def check_localchanges(self, repo, force=False, refresh=True):
407 (c, a, r, d, u) = repo.changes(None, None)
519 m, a, r, d = repo.status()[:4]
408 if c or a or d or r:
520 if m or a or r or d:
409 self.ui.write("Local changes found, refresh first\n")
521 if not force:
410 sys.exit(1)
522 if refresh:
523 raise util.Abort(_("local changes found, refresh first"))
524 else:
525 raise util.Abort(_("local changes found"))
526 return m, a, r, d
411 def new(self, repo, patch, msg=None, force=None):
527 def new(self, repo, patch, msg=None, force=None):
412 commitfiles = []
528 if os.path.exists(self.join(patch)):
413 (c, a, r, d, u) = repo.changes(None, None)
529 raise util.Abort(_('patch "%s" already exists') % patch)
414 if c or a or d or r:
530 m, a, r, d = self.check_localchanges(repo, force)
415 if not force:
531 commitfiles = m + a + r
416 raise util.Abort(_("Local changes found, refresh first"))
417 else:
418 commitfiles = c + a + r
419 self.check_toppatch(repo)
532 self.check_toppatch(repo)
420 wlock = repo.wlock()
533 wlock = repo.wlock()
421 insert = self.series_end()
534 insert = self.full_series_end()
422 if msg:
535 if msg:
423 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
536 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
424 wlock=wlock)
537 wlock=wlock)
@@ -426,11 +539,10 b' class queue:'
426 n = repo.commit(commitfiles,
539 n = repo.commit(commitfiles,
427 "New patch: %s" % patch, force=True, wlock=wlock)
540 "New patch: %s" % patch, force=True, wlock=wlock)
428 if n == None:
541 if n == None:
429 self.ui.warn("repo commit failed\n")
542 raise util.Abort(_("repo commit failed"))
430 sys.exit(1)
431 self.full_series[insert:insert] = [patch]
543 self.full_series[insert:insert] = [patch]
432 self.applied.append(revlog.hex(n) + ":" + patch)
544 self.applied.append(statusentry(revlog.hex(n), patch))
433 self.read_series(self.full_series)
545 self.parse_series()
434 self.series_dirty = 1
546 self.series_dirty = 1
435 self.applied_dirty = 1
547 self.applied_dirty = 1
436 p = self.opener(patch, "w")
548 p = self.opener(patch, "w")
@@ -509,9 +621,9 b' class queue:'
509 # 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
510 # sensible order. When stripping many files, this helps keep
622 # sensible order. When stripping many files, this helps keep
511 # our disk access patterns under control.
623 # our disk access patterns under control.
512 list = seen.keys()
624 seen_list = seen.keys()
513 list.sort()
625 seen_list.sort()
514 for f in list:
626 for f in seen_list:
515 ff = repo.file(f)
627 ff = repo.file(f)
516 filerev = seen[f]
628 filerev = seen[f]
517 if filerev != 0:
629 if filerev != 0:
@@ -530,8 +642,9 b' class queue:'
530 revnum = chlog.rev(rev)
642 revnum = chlog.rev(rev)
531
643
532 if update:
644 if update:
645 self.check_localchanges(repo, refresh=False)
533 urev = self.qparents(repo, rev)
646 urev = self.qparents(repo, rev)
534 repo.update(urev, allow=False, force=True, wlock=wlock)
647 hg.clean(repo, urev, wlock=wlock)
535 repo.dirstate.write()
648 repo.dirstate.write()
536
649
537 # 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
@@ -540,7 +653,6 b' class queue:'
540 saveheads = []
653 saveheads = []
541 savebases = {}
654 savebases = {}
542
655
543 tip = chlog.tip()
544 heads = limitheads(chlog, rev)
656 heads = limitheads(chlog, rev)
545 seen = {}
657 seen = {}
546
658
@@ -571,7 +683,7 b' class queue:'
571 savebases[x] = 1
683 savebases[x] = 1
572
684
573 # create a changegroup for all the branches we need to keep
685 # create a changegroup for all the branches we need to keep
574 if backup is "all":
686 if backup == "all":
575 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
687 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
576 bundle(backupch)
688 bundle(backupch)
577 if saveheads:
689 if saveheads:
@@ -586,37 +698,89 b' class queue:'
586 if saveheads:
698 if saveheads:
587 self.ui.status("adding branch\n")
699 self.ui.status("adding branch\n")
588 commands.unbundle(self.ui, repo, chgrpfile, update=False)
700 commands.unbundle(self.ui, repo, chgrpfile, update=False)
589 if backup is not "strip":
701 if backup != "strip":
590 os.unlink(chgrpfile)
702 os.unlink(chgrpfile)
591
703
592 def isapplied(self, patch):
704 def isapplied(self, patch):
593 """returns (index, rev, patch)"""
705 """returns (index, rev, patch)"""
594 for i in xrange(len(self.applied)):
706 for i in xrange(len(self.applied)):
595 p = self.applied[i]
707 a = self.applied[i]
596 a = p.split(':')
708 if a.name == patch:
597 if a[1] == patch:
709 return (i, a.rev, a.name)
598 return (i, a[0], a[1])
599 return None
710 return None
600
711
601 def lookup(self, patch):
712 # if the exact patch name does not exist, we try a few
713 # variations. If strict is passed, we try only #1
714 #
715 # 1) a number to indicate an offset in the series file
716 # 2) a unique substring of the patch name was given
717 # 3) patchname[-+]num to indicate an offset in the series file
718 def lookup(self, patch, strict=False):
719 patch = patch and str(patch)
720
721 def partial_name(s):
722 if s in self.series:
723 return s
724 matches = [x for x in self.series if s in x]
725 if len(matches) > 1:
726 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
727 for m in matches:
728 self.ui.warn(' %s\n' % m)
729 return None
730 if matches:
731 return matches[0]
732 if len(self.series) > 0 and len(self.applied) > 0:
733 if s == 'qtip':
734 return self.series[self.series_end()-1]
735 if s == 'qbase':
736 return self.series[0]
737 return None
602 if patch == None:
738 if patch == None:
603 return None
739 return None
604 if patch in self.series:
740
605 return patch
741 # we don't want to return a partial match until we make
606 if not os.path.isfile(os.path.join(self.path, patch)):
742 # sure the file name passed in does not exist (checked below)
743 res = partial_name(patch)
744 if res and res == patch:
745 return res
746
747 if not os.path.isfile(self.join(patch)):
607 try:
748 try:
608 sno = int(patch)
749 sno = int(patch)
609 except(ValueError, OverflowError):
750 except(ValueError, OverflowError):
610 self.ui.warn("patch %s not in series\n" % patch)
751 pass
611 sys.exit(1)
752 else:
612 if sno >= len(self.series):
753 if sno < len(self.series):
613 self.ui.warn("patch number %d is out of range\n" % sno)
754 return self.series[sno]
614 sys.exit(1)
755 if not strict:
615 patch = self.series[sno]
756 # return any partial match made above
616 else:
757 if res:
617 self.ui.warn("patch %s not in series\n" % patch)
758 return res
618 sys.exit(1)
759 minus = patch.rsplit('-', 1)
619 return patch
760 if len(minus) > 1:
761 res = partial_name(minus[0])
762 if res:
763 i = self.series.index(res)
764 try:
765 off = int(minus[1] or 1)
766 except(ValueError, OverflowError):
767 pass
768 else:
769 if i - off >= 0:
770 return self.series[i - off]
771 plus = patch.rsplit('+', 1)
772 if len(plus) > 1:
773 res = partial_name(plus[0])
774 if res:
775 i = self.series.index(res)
776 try:
777 off = int(plus[1] or 1)
778 except(ValueError, OverflowError):
779 pass
780 else:
781 if i + off < len(self.series):
782 return self.series[i + off]
783 raise util.Abort(_("patch %s not in series") % patch)
620
784
621 def push(self, repo, patch=None, force=False, list=False,
785 def push(self, repo, patch=None, force=False, list=False,
622 mergeq=None, wlock=None):
786 mergeq=None, wlock=None):
@@ -624,10 +788,10 b' class queue:'
624 wlock = repo.wlock()
788 wlock = repo.wlock()
625 patch = self.lookup(patch)
789 patch = self.lookup(patch)
626 if patch and self.isapplied(patch):
790 if patch and self.isapplied(patch):
627 self.ui.warn("patch %s is already applied\n" % patch)
791 self.ui.warn(_("patch %s is already applied\n") % patch)
628 sys.exit(1)
792 sys.exit(1)
629 if self.series_end() == len(self.series):
793 if self.series_end() == len(self.series):
630 self.ui.warn("File series fully applied\n")
794 self.ui.warn(_("patch series fully applied\n"))
631 sys.exit(1)
795 sys.exit(1)
632 if not force:
796 if not force:
633 self.check_localchanges(repo)
797 self.check_localchanges(repo)
@@ -646,7 +810,7 b' class queue:'
646 ret = self.mergepatch(repo, mergeq, s, wlock)
810 ret = self.mergepatch(repo, mergeq, s, wlock)
647 else:
811 else:
648 ret = self.apply(repo, s, list, wlock=wlock)
812 ret = self.apply(repo, s, list, wlock=wlock)
649 top = self.applied[-1].split(':')[1]
813 top = self.applied[-1].name
650 if ret[0]:
814 if ret[0]:
651 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" %
652 top)
816 top)
@@ -654,7 +818,8 b' class queue:'
654 self.ui.write("Now at: %s\n" % top)
818 self.ui.write("Now at: %s\n" % top)
655 return ret[0]
819 return ret[0]
656
820
657 def pop(self, repo, patch=None, force=False, update=True, wlock=None):
821 def pop(self, repo, patch=None, force=False, update=True, all=False,
822 wlock=None):
658 def getfile(f, rev):
823 def getfile(f, rev):
659 t = repo.file(f).read(rev)
824 t = repo.file(f).read(rev)
660 try:
825 try:
@@ -675,15 +840,14 b' class queue:'
675 patch = self.lookup(patch)
840 patch = self.lookup(patch)
676 info = self.isapplied(patch)
841 info = self.isapplied(patch)
677 if not info:
842 if not info:
678 self.ui.warn("patch %s is not applied\n" % patch)
843 raise util.Abort(_("patch %s is not applied") % patch)
679 sys.exit(1)
680 if len(self.applied) == 0:
844 if len(self.applied) == 0:
681 self.ui.warn("No patches applied\n")
845 self.ui.warn(_("no patches applied\n"))
682 sys.exit(1)
846 sys.exit(1)
683
847
684 if not update:
848 if not update:
685 parents = repo.dirstate.parents()
849 parents = repo.dirstate.parents()
686 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
850 rr = [ revlog.bin(x.rev) for x in self.applied ]
687 for p in parents:
851 for p in parents:
688 if p in rr:
852 if p in rr:
689 self.ui.warn("qpop: forcing dirstate update\n")
853 self.ui.warn("qpop: forcing dirstate update\n")
@@ -695,7 +859,17 b' class queue:'
695 self.applied_dirty = 1;
859 self.applied_dirty = 1;
696 end = len(self.applied)
860 end = len(self.applied)
697 if not patch:
861 if not patch:
698 info = [len(self.applied) - 1] + self.applied[-1].split(':')
862 if all:
863 popi = 0
864 else:
865 popi = len(self.applied) - 1
866 else:
867 popi = info[0] + 1
868 if popi >= end:
869 self.ui.warn("qpop: %s is already at the top\n" % patch)
870 return
871 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
872
699 start = info[0]
873 start = info[0]
700 rev = revlog.bin(info[1])
874 rev = revlog.bin(info[1])
701
875
@@ -705,17 +879,16 b' class queue:'
705 top = self.check_toppatch(repo)
879 top = self.check_toppatch(repo)
706 qp = self.qparents(repo, rev)
880 qp = self.qparents(repo, rev)
707 changes = repo.changelog.read(qp)
881 changes = repo.changelog.read(qp)
708 mf1 = repo.manifest.readflags(changes[0])
709 mmap = repo.manifest.read(changes[0])
882 mmap = repo.manifest.read(changes[0])
710 (c, a, r, d, u) = repo.changes(qp, top)
883 m, a, r, d, u = repo.status(qp, top)[:5]
711 if d:
884 if d:
712 raise util.Abort("deletions found between repo revs")
885 raise util.Abort("deletions found between repo revs")
713 for f in c:
886 for f in m:
714 getfile(f, mmap[f])
887 getfile(f, mmap[f])
715 for f in r:
888 for f in r:
716 getfile(f, mmap[f])
889 getfile(f, mmap[f])
717 util.set_exec(repo.wjoin(f), mf1[f])
890 util.set_exec(repo.wjoin(f), mmap.execf(f))
718 repo.dirstate.update(c + r, 'n')
891 repo.dirstate.update(m + r, 'n')
719 for f in a:
892 for f in a:
720 try: os.unlink(repo.wjoin(f))
893 try: os.unlink(repo.wjoin(f))
721 except: raise
894 except: raise
@@ -727,36 +900,46 b' class queue:'
727 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
900 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
728 del self.applied[start:end]
901 del self.applied[start:end]
729 if len(self.applied):
902 if len(self.applied):
730 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
903 self.ui.write("Now at: %s\n" % self.applied[-1].name)
731 else:
904 else:
732 self.ui.write("Patch queue now empty\n")
905 self.ui.write("Patch queue now empty\n")
733
906
734 def diff(self, repo, files):
907 def diff(self, repo, pats, opts):
735 top = self.check_toppatch(repo)
908 top = self.check_toppatch(repo)
736 if not top:
909 if not top:
737 self.ui.write("No patches applied\n")
910 self.ui.write("No patches applied\n")
738 return
911 return
739 qp = self.qparents(repo, top)
912 qp = self.qparents(repo, top)
740 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
913 self.printdiff(repo, qp, files=pats, opts=opts)
741
914
742 def refresh(self, repo, short=False):
915 def refresh(self, repo, pats=None, **opts):
743 if len(self.applied) == 0:
916 if len(self.applied) == 0:
744 self.ui.write("No patches applied\n")
917 self.ui.write("No patches applied\n")
745 return
918 return
746 wlock = repo.wlock()
919 wlock = repo.wlock()
747 self.check_toppatch(repo)
920 self.check_toppatch(repo)
748 qp = self.qparents(repo)
921 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
749 (top, patch) = self.applied[-1].split(':')
750 top = revlog.bin(top)
922 top = revlog.bin(top)
751 cparents = repo.changelog.parents(top)
923 cparents = repo.changelog.parents(top)
752 patchparent = self.qparents(repo, top)
924 patchparent = self.qparents(repo, top)
753 message, comments, user, date, patchfound = self.readheaders(patch)
925 message, comments, user, date, patchfound = self.readheaders(patch)
754
926
755 patchf = self.opener(patch, "w")
927 patchf = self.opener(patch, "w")
928 msg = opts.get('msg', '').rstrip()
929 if msg:
930 if comments:
931 # Remove existing message.
932 ci = 0
933 for mi in range(len(message)):
934 while message[mi] != comments[ci]:
935 ci += 1
936 del comments[ci]
937 comments.append(msg)
756 if comments:
938 if comments:
757 comments = "\n".join(comments) + '\n\n'
939 comments = "\n".join(comments) + '\n\n'
758 patchf.write(comments)
940 patchf.write(comments)
759
941
942 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
760 tip = repo.changelog.tip()
943 tip = repo.changelog.tip()
761 if top == tip:
944 if top == tip:
762 # 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
@@ -769,30 +952,30 b' class queue:'
769 # patch already
952 # patch already
770 #
953 #
771 # this should really read:
954 # this should really read:
772 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
955 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
773 # but we do it backwards to take advantage of manifest/chlog
956 # but we do it backwards to take advantage of manifest/chlog
774 # caching against the next repo.changes call
957 # caching against the next repo.status call
775 #
958 #
776 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
959 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
777 if short:
960 if opts.get('short'):
778 filelist = cc + aa + dd
961 filelist = mm + aa + dd
779 else:
962 else:
780 filelist = None
963 filelist = None
781 (c, a, r, d, u) = repo.changes(None, None, filelist)
964 m, a, r, d, u = repo.status(files=filelist)[:5]
782
965
783 # 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
784 # the dirstate parent, but then changed in the local dirstate.
967 # the dirstate parent, but then changed in the local dirstate.
785 # 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
786 for x in c:
969 for x in m:
787 if x not in aa:
970 if x not in aa:
788 cc.append(x)
971 mm.append(x)
789 # 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
790 # were deleted by the patch. In this case, they should only
973 # were deleted by the patch. In this case, they should only
791 # show up in the changed section.
974 # show up in the changed section.
792 for x in a:
975 for x in a:
793 if x in dd:
976 if x in dd:
794 del dd[dd.index(x)]
977 del dd[dd.index(x)]
795 cc.append(x)
978 mm.append(x)
796 else:
979 else:
797 aa.append(x)
980 aa.append(x)
798 # make sure any files deleted in the local dirstate
981 # make sure any files deleted in the local dirstate
@@ -803,70 +986,97 b' class queue:'
803 del aa[aa.index(x)]
986 del aa[aa.index(x)]
804 forget.append(x)
987 forget.append(x)
805 continue
988 continue
806 elif x in cc:
989 elif x in mm:
807 del cc[cc.index(x)]
990 del mm[mm.index(x)]
808 dd.append(x)
991 dd.append(x)
809
992
810 c = list(util.unique(cc))
993 m = list(util.unique(mm))
811 r = list(util.unique(dd))
994 r = list(util.unique(dd))
812 a = list(util.unique(aa))
995 a = list(util.unique(aa))
813 filelist = list(util.unique(c + r + a ))
996 filelist = filter(matchfn, util.unique(m + r + a))
814 commands.dodiff(patchf, self.ui, repo, patchparent, None,
997 self.printdiff(repo, patchparent, files=filelist,
815 filelist, changes=(c, a, r, [], u))
998 changes=(m, a, r, [], u), fp=patchf)
816 patchf.close()
999 patchf.close()
817
1000
818 changes = repo.changelog.read(tip)
1001 changes = repo.changelog.read(tip)
819 repo.dirstate.setparents(*cparents)
1002 repo.dirstate.setparents(*cparents)
1003 copies = [(f, repo.dirstate.copied(f)) for f in a]
820 repo.dirstate.update(a, 'a')
1004 repo.dirstate.update(a, 'a')
1005 for dst, src in copies:
1006 repo.dirstate.copy(src, dst)
821 repo.dirstate.update(r, 'r')
1007 repo.dirstate.update(r, 'r')
822 repo.dirstate.update(c, 'n')
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]
1015 repo.dirstate.update(m, 'n')
1016 repo.dirstate.update(mm, 'n', st_mtime=0)
823 repo.dirstate.forget(forget)
1017 repo.dirstate.forget(forget)
824
1018
825 if not message:
1019 if not msg:
826 message = "patch queue: %s\n" % patch
1020 if not message:
1021 message = "patch queue: %s\n" % patch
1022 else:
1023 message = "\n".join(message)
827 else:
1024 else:
828 message = "\n".join(message)
1025 message = msg
1026
829 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1027 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
830 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1028 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
831 self.applied[-1] = revlog.hex(n) + ':' + patch
1029 self.applied[-1] = statusentry(revlog.hex(n), patch)
832 self.applied_dirty = 1
1030 self.applied_dirty = 1
833 else:
1031 else:
834 commands.dodiff(patchf, self.ui, repo, patchparent, None)
1032 self.printdiff(repo, patchparent, fp=patchf)
835 patchf.close()
1033 patchf.close()
836 self.pop(repo, force=True, wlock=wlock)
1034 self.pop(repo, force=True, wlock=wlock)
837 self.push(repo, force=True, wlock=wlock)
1035 self.push(repo, force=True, wlock=wlock)
838
1036
839 def init(self, repo, create=False):
1037 def init(self, repo, create=False):
840 if os.path.isdir(self.path):
1038 if os.path.isdir(self.path):
841 raise util.Abort("patch queue directory already exists")
1039 raise util.Abort(_("patch queue directory already exists"))
842 os.mkdir(self.path)
1040 os.mkdir(self.path)
843 if create:
1041 if create:
844 return self.qrepo(create=True)
1042 return self.qrepo(create=True)
845
1043
846 def unapplied(self, repo, patch=None):
1044 def unapplied(self, repo, patch=None):
847 if patch and patch not in self.series:
1045 if patch and patch not in self.series:
848 self.ui.warn("%s not in the series file\n" % patch)
1046 raise util.Abort(_("patch %s is not in series file") % patch)
849 sys.exit(1)
850 if not patch:
1047 if not patch:
851 start = self.series_end()
1048 start = self.series_end()
852 else:
1049 else:
853 start = self.series.index(patch) + 1
1050 start = self.series.index(patch) + 1
854 for p in self.series[start:]:
1051 unapplied = []
855 self.ui.write("%s\n" % p)
1052 for i in xrange(start, len(self.series)):
1053 pushable, reason = self.pushable(i)
1054 if pushable:
1055 unapplied.append((i, self.series[i]))
1056 self.explain_pushable(i)
1057 return unapplied
856
1058
857 def qseries(self, repo, missing=None):
1059 def qseries(self, repo, missing=None, summary=False):
858 start = self.series_end()
1060 start = self.series_end(all_patches=True)
859 if not missing:
1061 if not missing:
860 for p in self.series[:start]:
1062 for i in range(len(self.series)):
1063 patch = self.series[i]
861 if self.ui.verbose:
1064 if self.ui.verbose:
862 self.ui.write("%d A " % self.series.index(p))
1065 if i < start:
863 self.ui.write("%s\n" % p)
1066 status = 'A'
864 for p in self.series[start:]:
1067 elif self.pushable(i)[0]:
865 if self.ui.verbose:
1068 status = 'U'
866 self.ui.write("%d U " % self.series.index(p))
1069 else:
867 self.ui.write("%s\n" % p)
1070 status = 'G'
1071 self.ui.write('%d %s ' % (i, status))
1072 if summary:
1073 msg = self.readheaders(patch)[0]
1074 msg = msg and ': ' + msg[0] or ': '
1075 else:
1076 msg = ''
1077 self.ui.write('%s%s\n' % (patch, msg))
868 else:
1078 else:
869 list = []
1079 msng_list = []
870 for root, dirs, files in os.walk(self.path):
1080 for root, dirs, files in os.walk(self.path):
871 d = root[len(self.path) + 1:]
1081 d = root[len(self.path) + 1:]
872 for f in files:
1082 for f in files:
@@ -874,21 +1084,19 b' class queue:'
874 if (fl not in self.series and
1084 if (fl not in self.series and
875 fl not in (self.status_path, self.series_path)
1085 fl not in (self.status_path, self.series_path)
876 and not fl.startswith('.')):
1086 and not fl.startswith('.')):
877 list.append(fl)
1087 msng_list.append(fl)
878 list.sort()
1088 msng_list.sort()
879 if list:
1089 for x in msng_list:
880 for x in list:
1090 if self.ui.verbose:
881 if self.ui.verbose:
1091 self.ui.write("D ")
882 self.ui.write("D ")
1092 self.ui.write("%s\n" % x)
883 self.ui.write("%s\n" % x)
884
1093
885 def issaveline(self, l):
1094 def issaveline(self, l):
886 name = l.split(':')[1]
1095 if l.name == '.hg.patches.save.line':
887 if name == '.hg.patches.save.line':
888 return True
1096 return True
889
1097
890 def qrepo(self, create=False):
1098 def qrepo(self, create=False):
891 if create or os.path.isdir(os.path.join(self.path, ".hg")):
1099 if create or os.path.isdir(self.join(".hg")):
892 return hg.repository(self.ui, path=self.path, create=create)
1100 return hg.repository(self.ui, path=self.path, create=create)
893
1101
894 def restore(self, repo, rev, delete=None, qupdate=None):
1102 def restore(self, repo, rev, delete=None, qupdate=None):
@@ -909,19 +1117,18 b' class queue:'
909 qpp = [ hg.bin(x) for x in l ]
1117 qpp = [ hg.bin(x) for x in l ]
910 elif datastart != None:
1118 elif datastart != None:
911 l = lines[i].rstrip()
1119 l = lines[i].rstrip()
912 index = l.index(':')
1120 se = statusentry(l)
913 id = l[:index]
1121 file_ = se.name
914 file = l[index + 1:]
1122 if se.rev:
915 if id:
1123 applied.append(se)
916 applied.append(l)
1124 series.append(file_)
917 series.append(file)
918 if datastart == None:
1125 if datastart == None:
919 self.ui.warn("No saved patch data found\n")
1126 self.ui.warn("No saved patch data found\n")
920 return 1
1127 return 1
921 self.ui.warn("restoring status: %s\n" % lines[0])
1128 self.ui.warn("restoring status: %s\n" % lines[0])
922 self.full_series = series
1129 self.full_series = series
923 self.applied = applied
1130 self.applied = applied
924 self.read_series(self.full_series)
1131 self.parse_series()
925 self.series_dirty = 1
1132 self.series_dirty = 1
926 self.applied_dirty = 1
1133 self.applied_dirty = 1
927 heads = repo.changelog.heads()
1134 heads = repo.changelog.heads()
@@ -945,7 +1152,7 b' class queue:'
945 if not r:
1152 if not r:
946 self.ui.warn("Unable to load queue repository\n")
1153 self.ui.warn("Unable to load queue repository\n")
947 return 1
1154 return 1
948 r.update(qpp[0], allow=False, force=True)
1155 hg.clean(r, qpp[0])
949
1156
950 def save(self, repo, msg=None):
1157 def save(self, repo, msg=None):
951 if len(self.applied) == 0:
1158 if len(self.applied) == 0:
@@ -965,30 +1172,49 b' class queue:'
965 pp = r.dirstate.parents()
1172 pp = r.dirstate.parents()
966 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]))
967 msg += "\n\nPatch Data:\n"
1174 msg += "\n\nPatch Data:\n"
968 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1175 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
969 + '\n' or "")
1176 "\n".join(ar) + '\n' or "")
970 n = repo.commit(None, text, user=None, force=1)
1177 n = repo.commit(None, text, user=None, force=1)
971 if not n:
1178 if not n:
972 self.ui.warn("repo commit failed\n")
1179 self.ui.warn("repo commit failed\n")
973 return 1
1180 return 1
974 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1181 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
975 self.applied_dirty = 1
1182 self.applied_dirty = 1
976
1183
977 def series_end(self):
1184 def full_series_end(self):
1185 if len(self.applied) > 0:
1186 p = self.applied[-1].name
1187 end = self.find_series(p)
1188 if end == None:
1189 return len(self.full_series)
1190 return end + 1
1191 return 0
1192
1193 def series_end(self, all_patches=False):
978 end = 0
1194 end = 0
1195 def next(start):
1196 if all_patches:
1197 return start
1198 i = start
1199 while i < len(self.series):
1200 p, reason = self.pushable(i)
1201 if p:
1202 break
1203 self.explain_pushable(i)
1204 i += 1
1205 return i
979 if len(self.applied) > 0:
1206 if len(self.applied) > 0:
980 (top, p) = self.applied[-1].split(':')
1207 p = self.applied[-1].name
981 try:
1208 try:
982 end = self.series.index(p)
1209 end = self.series.index(p)
983 except ValueError:
1210 except ValueError:
984 return 0
1211 return 0
985 return end + 1
1212 return next(end + 1)
986 return end
1213 return next(end)
987
1214
988 def qapplied(self, repo, patch=None):
1215 def qapplied(self, repo, patch=None):
989 if patch and patch not in self.series:
1216 if patch and patch not in self.series:
990 self.ui.warn("%s not in the series file\n" % patch)
1217 raise util.Abort(_("patch %s is not in series file") % patch)
991 sys.exit(1)
992 if not patch:
1218 if not patch:
993 end = len(self.applied)
1219 end = len(self.applied)
994 else:
1220 else:
@@ -998,9 +1224,11 b' class queue:'
998 self.ui.write("%s\n" % p)
1224 self.ui.write("%s\n" % p)
999
1225
1000 def appliedname(self, index):
1226 def appliedname(self, index):
1001 p = self.applied[index]
1227 pname = self.applied[index].name
1002 if not self.ui.verbose:
1228 if not self.ui.verbose:
1003 p = p.split(':')[1]
1229 p = pname
1230 else:
1231 p = str(self.series.index(pname)) + " " + pname
1004 return p
1232 return p
1005
1233
1006 def top(self, repo):
1234 def top(self, repo):
@@ -1015,7 +1243,10 b' class queue:'
1015 if end == len(self.series):
1243 if end == len(self.series):
1016 self.ui.write("All patches applied\n")
1244 self.ui.write("All patches applied\n")
1017 else:
1245 else:
1018 self.ui.write(self.series[end] + '\n')
1246 p = self.series[end]
1247 if self.ui.verbose:
1248 self.ui.write("%d " % self.series.index(p))
1249 self.ui.write(p + '\n')
1019
1250
1020 def prev(self, repo):
1251 def prev(self, repo):
1021 if len(self.applied) > 1:
1252 if len(self.applied) > 1:
@@ -1028,36 +1259,33 b' class queue:'
1028
1259
1029 def qimport(self, repo, files, patch=None, existing=None, force=None):
1260 def qimport(self, repo, files, patch=None, existing=None, force=None):
1030 if len(files) > 1 and patch:
1261 if len(files) > 1 and patch:
1031 self.ui.warn("-n option not valid when importing multiple files\n")
1262 raise util.Abort(_('option "-n" not valid when importing multiple '
1032 sys.exit(1)
1263 'files'))
1033 i = 0
1264 i = 0
1034 added = []
1265 added = []
1035 for filename in files:
1266 for filename in files:
1036 if existing:
1267 if existing:
1037 if not patch:
1268 if not patch:
1038 patch = filename
1269 patch = filename
1039 if not os.path.isfile(os.path.join(self.path, patch)):
1270 if not os.path.isfile(self.join(patch)):
1040 self.ui.warn("patch %s does not exist\n" % patch)
1271 raise util.Abort(_("patch %s does not exist") % patch)
1041 sys.exit(1)
1042 else:
1272 else:
1043 try:
1273 try:
1044 text = file(filename).read()
1274 text = file(filename).read()
1045 except IOError:
1275 except IOError:
1046 self.ui.warn("Unable to read %s\n" % patch)
1276 raise util.Abort(_("unable to read %s") % patch)
1047 sys.exit(1)
1048 if not patch:
1277 if not patch:
1049 patch = os.path.split(filename)[1]
1278 patch = os.path.split(filename)[1]
1050 if not force and os.path.isfile(os.path.join(self.path, patch)):
1279 if not force and os.path.exists(self.join(patch)):
1051 self.ui.warn("patch %s already exists\n" % patch)
1280 raise util.Abort(_('patch "%s" already exists') % patch)
1052 sys.exit(1)
1053 patchf = self.opener(patch, "w")
1281 patchf = self.opener(patch, "w")
1054 patchf.write(text)
1282 patchf.write(text)
1055 if patch in self.series:
1283 if patch in self.series:
1056 self.ui.warn("patch %s is already in the series file\n" % patch)
1284 raise util.Abort(_('patch %s is already in the series file')
1057 sys.exit(1)
1285 % patch)
1058 index = self.series_end() + i
1286 index = self.full_series_end() + i
1059 self.full_series[index:index] = [patch]
1287 self.full_series[index:index] = [patch]
1060 self.read_series(self.full_series)
1288 self.parse_series()
1061 self.ui.warn("adding %s to series file\n" % patch)
1289 self.ui.warn("adding %s to series file\n" % patch)
1062 i += 1
1290 i += 1
1063 added.append(patch)
1291 added.append(patch)
@@ -1067,34 +1295,44 b' class queue:'
1067 if qrepo:
1295 if qrepo:
1068 qrepo.add(added)
1296 qrepo.add(added)
1069
1297
1070 def delete(ui, repo, patch, **opts):
1298 def delete(ui, repo, patch, *patches, **opts):
1071 """remove a patch from the series file"""
1299 """remove patches from queue
1072 q = repomap[repo]
1300
1073 q.delete(repo, patch)
1301 The patches must not be applied.
1302 With -k, the patch files are preserved in the patch directory."""
1303 q = repo.mq
1304 q.delete(repo, (patch,) + patches, keep=opts.get('keep'))
1074 q.save_dirty()
1305 q.save_dirty()
1075 return 0
1306 return 0
1076
1307
1077 def applied(ui, repo, patch=None, **opts):
1308 def applied(ui, repo, patch=None, **opts):
1078 """print the patches already applied"""
1309 """print the patches already applied"""
1079 repomap[repo].qapplied(repo, patch)
1310 repo.mq.qapplied(repo, patch)
1080 return 0
1311 return 0
1081
1312
1082 def unapplied(ui, repo, patch=None, **opts):
1313 def unapplied(ui, repo, patch=None, **opts):
1083 """print the patches not yet applied"""
1314 """print the patches not yet applied"""
1084 repomap[repo].unapplied(repo, patch)
1315 for i, p in repo.mq.unapplied(repo, patch):
1085 return 0
1316 if ui.verbose:
1317 ui.write("%d " % i)
1318 ui.write("%s\n" % p)
1086
1319
1087 def qimport(ui, repo, *filename, **opts):
1320 def qimport(ui, repo, *filename, **opts):
1088 """import a patch"""
1321 """import a patch"""
1089 q = repomap[repo]
1322 q = repo.mq
1090 q.qimport(repo, filename, patch=opts['name'],
1323 q.qimport(repo, filename, patch=opts['name'],
1091 existing=opts['existing'], force=opts['force'])
1324 existing=opts['existing'], force=opts['force'])
1092 q.save_dirty()
1325 q.save_dirty()
1093 return 0
1326 return 0
1094
1327
1095 def init(ui, repo, **opts):
1328 def init(ui, repo, **opts):
1096 """init a new queue repository"""
1329 """init a new queue repository
1097 q = repomap[repo]
1330
1331 The queue repository is unversioned by default. If -c is
1332 specified, qinit will create a separate nested repository
1333 for patches. Use qcommit to commit changes to this queue
1334 repository."""
1335 q = repo.mq
1098 r = q.init(repo, create=opts['create_repo'])
1336 r = q.init(repo, create=opts['create_repo'])
1099 q.save_dirty()
1337 q.save_dirty()
1100 if r:
1338 if r:
@@ -1106,68 +1344,254 b' def init(ui, repo, **opts):'
1106 r.add(['.hgignore', 'series'])
1344 r.add(['.hgignore', 'series'])
1107 return 0
1345 return 0
1108
1346
1347 def clone(ui, source, dest=None, **opts):
1348 '''clone main and patch repository at same time
1349
1350 If source is local, destination will have no patches applied. If
1351 source is remote, this command can not check if patches are
1352 applied in source, so cannot guarantee that patches are not
1353 applied in destination. If you clone remote repository, be sure
1354 before that it has no patches applied.
1355
1356 Source patch repository is looked for in <src>/.hg/patches by
1357 default. Use -p <url> to change.
1358 '''
1359 commands.setremoteconfig(ui, opts)
1360 if dest is None:
1361 dest = hg.defaultdest(source)
1362 sr = hg.repository(ui, ui.expandpath(source))
1363 qbase, destrev = None, None
1364 if sr.local():
1365 reposetup(ui, sr)
1366 if sr.mq.applied:
1367 qbase = revlog.bin(sr.mq.applied[0].rev)
1368 if not hg.islocal(dest):
1369 destrev = sr.parents(qbase)[0]
1370 ui.note(_('cloning main repo\n'))
1371 sr, dr = hg.clone(ui, sr, dest,
1372 pull=opts['pull'],
1373 rev=destrev,
1374 update=False,
1375 stream=opts['uncompressed'])
1376 ui.note(_('cloning patch repo\n'))
1377 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1378 dr.url() + '/.hg/patches',
1379 pull=opts['pull'],
1380 update=not opts['noupdate'],
1381 stream=opts['uncompressed'])
1382 if dr.local():
1383 if qbase:
1384 ui.note(_('stripping applied patches from destination repo\n'))
1385 reposetup(ui, dr)
1386 dr.mq.strip(dr, qbase, update=False, backup=None)
1387 if not opts['noupdate']:
1388 ui.note(_('updating destination repo\n'))
1389 hg.update(dr, dr.changelog.tip())
1390
1109 def commit(ui, repo, *pats, **opts):
1391 def commit(ui, repo, *pats, **opts):
1110 """commit changes in the queue repository"""
1392 """commit changes in the queue repository"""
1111 q = repomap[repo]
1393 q = repo.mq
1112 r = q.qrepo()
1394 r = q.qrepo()
1113 if not r: raise util.Abort('no queue repository')
1395 if not r: raise util.Abort('no queue repository')
1114 commands.commit(r.ui, r, *pats, **opts)
1396 commands.commit(r.ui, r, *pats, **opts)
1115
1397
1116 def series(ui, repo, **opts):
1398 def series(ui, repo, **opts):
1117 """print the entire series file"""
1399 """print the entire series file"""
1118 repomap[repo].qseries(repo, missing=opts['missing'])
1400 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1119 return 0
1401 return 0
1120
1402
1121 def top(ui, repo, **opts):
1403 def top(ui, repo, **opts):
1122 """print the name of the current patch"""
1404 """print the name of the current patch"""
1123 repomap[repo].top(repo)
1405 repo.mq.top(repo)
1124 return 0
1406 return 0
1125
1407
1126 def next(ui, repo, **opts):
1408 def next(ui, repo, **opts):
1127 """print the name of the next patch"""
1409 """print the name of the next patch"""
1128 repomap[repo].next(repo)
1410 repo.mq.next(repo)
1129 return 0
1411 return 0
1130
1412
1131 def prev(ui, repo, **opts):
1413 def prev(ui, repo, **opts):
1132 """print the name of the previous patch"""
1414 """print the name of the previous patch"""
1133 repomap[repo].prev(repo)
1415 repo.mq.prev(repo)
1134 return 0
1416 return 0
1135
1417
1136 def new(ui, repo, patch, **opts):
1418 def new(ui, repo, patch, **opts):
1137 """create a new patch"""
1419 """create a new patch
1138 q = repomap[repo]
1420
1139 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1421 qnew creates a new patch on top of the currently-applied patch
1422 (if any). It will refuse to run if there are any outstanding
1423 changes unless -f is specified, in which case the patch will
1424 be initialised with them.
1425
1426 -e, -m or -l set the patch header as well as the commit message.
1427 If none is specified, the patch header is empty and the
1428 commit message is 'New patch: PATCH'"""
1429 q = repo.mq
1430 message = commands.logmessage(opts)
1431 if opts['edit']:
1432 message = ui.edit(message, ui.username())
1433 q.new(repo, patch, msg=message, force=opts['force'])
1434 q.save_dirty()
1435 return 0
1436
1437 def refresh(ui, repo, *pats, **opts):
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 """
1444 q = repo.mq
1445 message = commands.logmessage(opts)
1446 if opts['edit']:
1447 if message:
1448 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1449 patch = q.applied[-1].name
1450 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1451 message = ui.edit('\n'.join(message), user or ui.username())
1452 q.refresh(repo, pats, msg=message, **opts)
1140 q.save_dirty()
1453 q.save_dirty()
1141 return 0
1454 return 0
1142
1455
1143 def refresh(ui, repo, **opts):
1456 def diff(ui, repo, *pats, **opts):
1144 """update the current patch"""
1457 """diff of the current patch"""
1145 q = repomap[repo]
1458 repo.mq.diff(repo, pats, opts)
1146 q.refresh(repo, short=opts['short'])
1147 q.save_dirty()
1148 return 0
1459 return 0
1149
1460
1150 def diff(ui, repo, *files, **opts):
1461 def fold(ui, repo, *files, **opts):
1151 """diff of the current patch"""
1462 """fold the named patches into the current patch
1152 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1463
1153 repomap[repo].diff(repo, list(files))
1464 Patches must not yet be applied. Each patch will be successively
1154 return 0
1465 applied to the current patch in the order given. If all the
1466 patches apply successfully, the current patch will be refreshed
1467 with the new cumulative patch, and the folded patches will
1468 be deleted. With -k/--keep, the folded patch files will not
1469 be removed afterwards.
1470
1471 The header for each folded patch will be concatenated with
1472 the current patch header, separated by a line of '* * *'."""
1473
1474 q = repo.mq
1475
1476 if not files:
1477 raise util.Abort(_('qfold requires at least one patch name'))
1478 if not q.check_toppatch(repo):
1479 raise util.Abort(_('No patches applied\n'))
1480
1481 message = commands.logmessage(opts)
1482 if opts['edit']:
1483 if message:
1484 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1485
1486 parent = q.lookup('qtip')
1487 patches = []
1488 messages = []
1489 for f in files:
1490 p = q.lookup(f)
1491 if p in patches or p == parent:
1492 ui.warn(_('Skipping already folded patch %s') % p)
1493 if q.isapplied(p):
1494 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1495 patches.append(p)
1496
1497 for p in patches:
1498 if not message:
1499 messages.append(q.readheaders(p)[0])
1500 pf = q.join(p)
1501 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1502 if not patchsuccess:
1503 raise util.Abort(_('Error folding patch %s') % p)
1504 patch.updatedir(ui, repo, files)
1505
1506 if not message:
1507 message, comments, user = q.readheaders(parent)[0:3]
1508 for msg in messages:
1509 message.append('* * *')
1510 message.extend(msg)
1511 message = '\n'.join(message)
1512
1513 if opts['edit']:
1514 message = ui.edit(message, user or ui.username())
1515
1516 q.refresh(repo, msg=message)
1517 q.delete(repo, patches, keep=opts['keep'])
1518 q.save_dirty()
1519
1520 def guard(ui, repo, *args, **opts):
1521 '''set or print guards for a patch
1522
1523 Guards control whether a patch can be pushed. A patch with no
1524 guards is always pushed. A patch with a positive guard ("+foo") is
1525 pushed only if the qselect command has activated it. A patch with
1526 a negative guard ("-foo") is never pushed if the qselect command
1527 has activated it.
1528
1529 With no arguments, print the currently active guards.
1530 With arguments, set guards for the named patch.
1531
1532 To set a negative guard "-foo" on topmost patch ("--" is needed so
1533 hg will not interpret "-foo" as an option):
1534 hg qguard -- -foo
1535
1536 To set guards on another patch:
1537 hg qguard other.patch +2.6.17 -stable
1538 '''
1539 def status(idx):
1540 guards = q.series_guards[idx] or ['unguarded']
1541 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1542 q = repo.mq
1543 patch = None
1544 args = list(args)
1545 if opts['list']:
1546 if args or opts['none']:
1547 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1548 for i in xrange(len(q.series)):
1549 status(i)
1550 return
1551 if not args or args[0][0:1] in '-+':
1552 if not q.applied:
1553 raise util.Abort(_('no patches applied'))
1554 patch = q.applied[-1].name
1555 if patch is None and args[0][0:1] not in '-+':
1556 patch = args.pop(0)
1557 if patch is None:
1558 raise util.Abort(_('no patch to work with'))
1559 if args or opts['none']:
1560 q.set_guards(q.find_series(patch), args)
1561 q.save_dirty()
1562 else:
1563 status(q.series.index(q.lookup(patch)))
1564
1565 def header(ui, repo, patch=None):
1566 """Print the header of the topmost or specified patch"""
1567 q = repo.mq
1568
1569 if patch:
1570 patch = q.lookup(patch)
1571 else:
1572 if not q.applied:
1573 ui.write('No patches applied\n')
1574 return
1575 patch = q.lookup('qtip')
1576 message = repo.mq.readheaders(patch)[0]
1577
1578 ui.write('\n'.join(message) + '\n')
1155
1579
1156 def lastsavename(path):
1580 def lastsavename(path):
1157 (dir, base) = os.path.split(path)
1581 (directory, base) = os.path.split(path)
1158 names = os.listdir(dir)
1582 names = os.listdir(directory)
1159 namere = re.compile("%s.([0-9]+)" % base)
1583 namere = re.compile("%s.([0-9]+)" % base)
1160 max = None
1584 maxindex = None
1161 maxname = None
1585 maxname = None
1162 for f in names:
1586 for f in names:
1163 m = namere.match(f)
1587 m = namere.match(f)
1164 if m:
1588 if m:
1165 index = int(m.group(1))
1589 index = int(m.group(1))
1166 if max == None or index > max:
1590 if maxindex == None or index > maxindex:
1167 max = index
1591 maxindex = index
1168 maxname = f
1592 maxname = f
1169 if maxname:
1593 if maxname:
1170 return (os.path.join(dir, maxname), max)
1594 return (os.path.join(directory, maxname), maxindex)
1171 return (None, None)
1595 return (None, None)
1172
1596
1173 def savename(path):
1597 def savename(path):
@@ -1179,7 +1603,7 b' def savename(path):'
1179
1603
1180 def push(ui, repo, patch=None, **opts):
1604 def push(ui, repo, patch=None, **opts):
1181 """push the next patch onto the stack"""
1605 """push the next patch onto the stack"""
1182 q = repomap[repo]
1606 q = repo.mq
1183 mergeq = None
1607 mergeq = None
1184
1608
1185 if opts['all']:
1609 if opts['all']:
@@ -1207,17 +1631,65 b' def pop(ui, repo, patch=None, **opts):'
1207 ui.warn('using patch queue: %s\n' % q.path)
1631 ui.warn('using patch queue: %s\n' % q.path)
1208 localupdate = False
1632 localupdate = False
1209 else:
1633 else:
1210 q = repomap[repo]
1634 q = repo.mq
1211 if opts['all'] and len(q.applied) > 0:
1635 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1212 patch = q.applied[0].split(':')[1]
1213 q.pop(repo, patch, force=opts['force'], update=localupdate)
1214 q.save_dirty()
1636 q.save_dirty()
1215 return 0
1637 return 0
1216
1638
1639 def rename(ui, repo, patch, name=None, **opts):
1640 """rename a patch
1641
1642 With one argument, renames the current patch to PATCH1.
1643 With two arguments, renames PATCH1 to PATCH2."""
1644
1645 q = repo.mq
1646
1647 if not name:
1648 name = patch
1649 patch = None
1650
1651 if name in q.series:
1652 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1653
1654 absdest = q.join(name)
1655 if os.path.exists(absdest):
1656 raise util.Abort(_('%s already exists') % absdest)
1657
1658 if patch:
1659 patch = q.lookup(patch)
1660 else:
1661 if not q.applied:
1662 ui.write(_('No patches applied\n'))
1663 return
1664 patch = q.lookup('qtip')
1665
1666 if ui.verbose:
1667 ui.write('Renaming %s to %s\n' % (patch, name))
1668 i = q.find_series(patch)
1669 q.full_series[i] = name
1670 q.parse_series()
1671 q.series_dirty = 1
1672
1673 info = q.isapplied(patch)
1674 if info:
1675 q.applied[info[0]] = statusentry(info[1], name)
1676 q.applied_dirty = 1
1677
1678 util.rename(q.join(patch), absdest)
1679 r = q.qrepo()
1680 if r:
1681 wlock = r.wlock()
1682 if r.dirstate.state(name) == 'r':
1683 r.undelete([name], wlock)
1684 r.copy(patch, name, wlock)
1685 r.remove([patch], False, wlock)
1686
1687 q.save_dirty()
1688
1217 def restore(ui, repo, rev, **opts):
1689 def restore(ui, repo, rev, **opts):
1218 """restore the queue state saved by a rev"""
1690 """restore the queue state saved by a rev"""
1219 rev = repo.lookup(rev)
1691 rev = repo.lookup(rev)
1220 q = repomap[repo]
1692 q = repo.mq
1221 q.restore(repo, rev, delete=opts['delete'],
1693 q.restore(repo, rev, delete=opts['delete'],
1222 qupdate=opts['update'])
1694 qupdate=opts['update'])
1223 q.save_dirty()
1695 q.save_dirty()
@@ -1225,8 +1697,9 b' def restore(ui, repo, rev, **opts):'
1225
1697
1226 def save(ui, repo, **opts):
1698 def save(ui, repo, **opts):
1227 """save current queue state"""
1699 """save current queue state"""
1228 q = repomap[repo]
1700 q = repo.mq
1229 ret = q.save(repo, msg=opts['message'])
1701 message = commands.logmessage(opts)
1702 ret = q.save(repo, msg=message)
1230 if ret:
1703 if ret:
1231 return ret
1704 return ret
1232 q.save_dirty()
1705 q.save_dirty()
@@ -1236,20 +1709,18 b' def save(ui, repo, **opts):'
1236 newpath = os.path.join(q.basepath, opts['name'])
1709 newpath = os.path.join(q.basepath, opts['name'])
1237 if os.path.exists(newpath):
1710 if os.path.exists(newpath):
1238 if not os.path.isdir(newpath):
1711 if not os.path.isdir(newpath):
1239 ui.warn("destination %s exists and is not a directory\n" %
1712 raise util.Abort(_('destination %s exists and is not '
1240 newpath)
1713 'a directory') % newpath)
1241 sys.exit(1)
1242 if not opts['force']:
1714 if not opts['force']:
1243 ui.warn("destination %s exists, use -f to force\n" %
1715 raise util.Abort(_('destination %s exists, '
1244 newpath)
1716 'use -f to force') % newpath)
1245 sys.exit(1)
1246 else:
1717 else:
1247 newpath = savename(path)
1718 newpath = savename(path)
1248 ui.warn("copy %s to %s\n" % (path, newpath))
1719 ui.warn("copy %s to %s\n" % (path, newpath))
1249 util.copyfiles(path, newpath)
1720 util.copyfiles(path, newpath)
1250 if opts['empty']:
1721 if opts['empty']:
1251 try:
1722 try:
1252 os.unlink(os.path.join(q.path, q.status_path))
1723 os.unlink(q.join(q.status_path))
1253 except:
1724 except:
1254 pass
1725 pass
1255 return 0
1726 return 0
@@ -1262,25 +1733,196 b' def strip(ui, repo, rev, **opts):'
1262 backup = 'strip'
1733 backup = 'strip'
1263 elif opts['nobackup']:
1734 elif opts['nobackup']:
1264 backup = 'none'
1735 backup = 'none'
1265 repomap[repo].strip(repo, rev, backup=backup)
1736 repo.mq.strip(repo, rev, backup=backup)
1266 return 0
1737 return 0
1267
1738
1268 def version(ui, q=None):
1739 def select(ui, repo, *args, **opts):
1269 """print the version number"""
1740 '''set or print guarded patches to push
1270 ui.write("mq version %s\n" % versionstr)
1741
1271 return 0
1742 Use the qguard command to set or print guards on patch, then use
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:
1747
1748 qguard foo.patch -stable (negative guard)
1749 qguard bar.patch +stable (positive guard)
1750 qselect stable
1751
1752 This activates the "stable" guard. mq will skip foo.patch (because
1753 it has a negative match) but push bar.patch (because it
1754 has a positive match).
1755
1756 With no arguments, prints the currently active guards.
1757 With one argument, sets the active guard.
1758
1759 Use -n/--none to deactivate guards (no other arguments needed).
1760 When no guards are active, patches with positive guards are skipped
1761 and patches with negative guards are pushed.
1762
1763 qselect can change the guards on applied patches. It does not pop
1764 guarded patches by default. Use --pop to pop back to the last applied
1765 patch that is not guarded. Use --reapply (which implies --pop) to push
1766 back to the current patch afterwards, but skip guarded patches.
1767
1768 Use -s/--series to print a list of all guards in the series file (no
1769 other arguments needed). Use -v for more information.'''
1770
1771 q = repo.mq
1772 guards = q.active()
1773 if args or opts['none']:
1774 old_unapplied = q.unapplied(repo)
1775 old_guarded = [i for i in xrange(len(q.applied)) if
1776 not q.pushable(i)[0]]
1777 q.set_active(args)
1778 q.save_dirty()
1779 if not args:
1780 ui.status(_('guards deactivated\n'))
1781 if not opts['pop'] and not opts['reapply']:
1782 unapplied = q.unapplied(repo)
1783 guarded = [i for i in xrange(len(q.applied))
1784 if not q.pushable(i)[0]]
1785 if len(unapplied) != len(old_unapplied):
1786 ui.status(_('number of unguarded, unapplied patches has '
1787 'changed from %d to %d\n') %
1788 (len(old_unapplied), len(unapplied)))
1789 if len(guarded) != len(old_guarded):
1790 ui.status(_('number of guarded, applied patches has changed '
1791 'from %d to %d\n') %
1792 (len(old_guarded), len(guarded)))
1793 elif opts['series']:
1794 guards = {}
1795 noguards = 0
1796 for gs in q.series_guards:
1797 if not gs:
1798 noguards += 1
1799 for g in gs:
1800 guards.setdefault(g, 0)
1801 guards[g] += 1
1802 if ui.verbose:
1803 guards['NONE'] = noguards
1804 guards = guards.items()
1805 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1806 if guards:
1807 ui.note(_('guards in series file:\n'))
1808 for guard, count in guards:
1809 ui.note('%2d ' % count)
1810 ui.write(guard, '\n')
1811 else:
1812 ui.note(_('no guards in series file\n'))
1813 else:
1814 if guards:
1815 ui.note(_('active guards:\n'))
1816 for g in guards:
1817 ui.write(g, '\n')
1818 else:
1819 ui.write(_('no active guards\n'))
1820 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1821 popped = False
1822 if opts['pop'] or opts['reapply']:
1823 for i in xrange(len(q.applied)):
1824 pushable, reason = q.pushable(i)
1825 if not pushable:
1826 ui.status(_('popping guarded patches\n'))
1827 popped = True
1828 if i == 0:
1829 q.pop(repo, all=True)
1830 else:
1831 q.pop(repo, i-1)
1832 break
1833 if popped:
1834 try:
1835 if reapply:
1836 ui.status(_('reapplying unguarded patches\n'))
1837 q.push(repo, reapply)
1838 finally:
1839 q.save_dirty()
1272
1840
1273 def reposetup(ui, repo):
1841 def reposetup(ui, repo):
1274 repomap[repo] = queue(ui, repo.join(""))
1842 class mqrepo(repo.__class__):
1843 def abort_if_wdir_patched(self, errmsg, force=False):
1844 if self.mq.applied and not force:
1845 parent = revlog.hex(self.dirstate.parents()[0])
1846 if parent in [s.rev for s in self.mq.applied]:
1847 raise util.Abort(errmsg)
1848
1849 def commit(self, *args, **opts):
1850 if len(args) >= 6:
1851 force = args[5]
1852 else:
1853 force = opts.get('force')
1854 self.abort_if_wdir_patched(
1855 _('cannot commit over an applied mq patch'),
1856 force)
1857
1858 return super(mqrepo, self).commit(*args, **opts)
1859
1860 def push(self, remote, force=False, revs=None):
1861 if self.mq.applied and not force:
1862 raise util.Abort(_('source has mq patches applied'))
1863 return super(mqrepo, self).push(remote, force, revs)
1864
1865 def tags(self):
1866 if self.tagscache:
1867 return self.tagscache
1868
1869 tagscache = super(mqrepo, self).tags()
1870
1871 q = self.mq
1872 if not q.applied:
1873 return tagscache
1874
1875 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1876 mqtags.append((mqtags[-1][0], 'qtip'))
1877 mqtags.append((mqtags[0][0], 'qbase'))
1878 for patch in mqtags:
1879 if patch[1] in tagscache:
1880 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1881 else:
1882 tagscache[patch[1]] = revlog.bin(patch[0])
1883
1884 return tagscache
1885
1886 if repo.local():
1887 repo.__class__ = mqrepo
1888 repo.mq = queue(ui, repo.join(""))
1275
1889
1276 cmdtable = {
1890 cmdtable = {
1277 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1891 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1892 "qclone": (clone,
1893 [('', 'pull', None, _('use pull protocol to copy metadata')),
1894 ('U', 'noupdate', None, _('do not update the new working directories')),
1895 ('', 'uncompressed', None,
1896 _('use uncompressed transfer (fast over LAN)')),
1897 ('e', 'ssh', '', _('specify ssh command to use')),
1898 ('p', 'patches', '', _('location of source patch repo')),
1899 ('', 'remotecmd', '',
1900 _('specify hg command to run on the remote side'))],
1901 'hg qclone [OPTION]... SOURCE [DEST]'),
1278 "qcommit|qci":
1902 "qcommit|qci":
1279 (commit,
1903 (commit,
1280 commands.table["^commit|ci"][1],
1904 commands.table["^commit|ci"][1],
1281 'hg qcommit [OPTION]... [FILE]...'),
1905 'hg qcommit [OPTION]... [FILE]...'),
1282 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1906 "^qdiff": (diff,
1283 "qdelete": (delete, [], 'hg qdelete PATCH'),
1907 [('I', 'include', [], _('include names matching the given patterns')),
1908 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1909 'hg qdiff [-I] [-X] [FILE]...'),
1910 "qdelete|qremove|qrm":
1911 (delete,
1912 [('k', 'keep', None, _('keep patch file'))],
1913 'hg qdelete [-k] PATCH'),
1914 'qfold':
1915 (fold,
1916 [('e', 'edit', None, _('edit patch header')),
1917 ('k', 'keep', None, _('keep folded patch files')),
1918 ('m', 'message', '', _('set patch header to <text>')),
1919 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1920 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1921 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
1922 ('n', 'none', None, _('drop all guards'))],
1923 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
1924 'qheader': (header, [],
1925 _('hg qheader [PATCH]')),
1284 "^qimport":
1926 "^qimport":
1285 (qimport,
1927 (qimport,
1286 [('e', 'existing', None, 'import file in patch dir'),
1928 [('e', 'existing', None, 'import file in patch dir'),
@@ -1293,9 +1935,11 b' cmdtable = {'
1293 'hg qinit [-c]'),
1935 'hg qinit [-c]'),
1294 "qnew":
1936 "qnew":
1295 (new,
1937 (new,
1296 [('m', 'message', '', 'commit message'),
1938 [('e', 'edit', None, _('edit commit message')),
1297 ('f', 'force', None, 'force')],
1939 ('m', 'message', '', _('use <text> as commit message')),
1298 'hg qnew [-m TEXT] [-f] PATCH'),
1940 ('l', 'logfile', '', _('read the commit message from <file>')),
1941 ('f', 'force', None, _('import uncommitted changes into patch'))],
1942 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
1299 "qnext": (next, [], 'hg qnext'),
1943 "qnext": (next, [], 'hg qnext'),
1300 "qprev": (prev, [], 'hg qprev'),
1944 "qprev": (prev, [], 'hg qprev'),
1301 "^qpop":
1945 "^qpop":
@@ -1314,8 +1958,15 b' cmdtable = {'
1314 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1958 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1315 "^qrefresh":
1959 "^qrefresh":
1316 (refresh,
1960 (refresh,
1317 [('s', 'short', None, 'short refresh')],
1961 [('e', 'edit', None, _('edit commit message')),
1318 'hg qrefresh [-s]'),
1962 ('m', 'message', '', _('change commit message with <text>')),
1963 ('l', 'logfile', '', _('change commit message with <file> content')),
1964 ('s', 'short', None, 'short refresh'),
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...'),
1968 'qrename|qmv':
1969 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1319 "qrestore":
1970 "qrestore":
1320 (restore,
1971 (restore,
1321 [('d', 'delete', None, 'delete save entry'),
1972 [('d', 'delete', None, 'delete save entry'),
@@ -1323,15 +1974,24 b' cmdtable = {'
1323 'hg qrestore [-d] [-u] REV'),
1974 'hg qrestore [-d] [-u] REV'),
1324 "qsave":
1975 "qsave":
1325 (save,
1976 (save,
1326 [('m', 'message', '', 'commit message'),
1977 [('m', 'message', '', _('use <text> as commit message')),
1978 ('l', 'logfile', '', _('read the commit message from <file>')),
1327 ('c', 'copy', None, 'copy patch directory'),
1979 ('c', 'copy', None, 'copy patch directory'),
1328 ('n', 'name', '', 'copy directory name'),
1980 ('n', 'name', '', 'copy directory name'),
1329 ('e', 'empty', None, 'clear queue status file'),
1981 ('e', 'empty', None, 'clear queue status file'),
1330 ('f', 'force', None, 'force copy')],
1982 ('f', 'force', None, 'force copy')],
1331 'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'),
1983 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1984 "qselect": (select,
1985 [('n', 'none', None, _('disable all guards')),
1986 ('s', 'series', None, _('list all guards in series file')),
1987 ('', 'pop', None,
1988 _('pop to before first guarded applied patch')),
1989 ('', 'reapply', None, _('pop, then reapply patches'))],
1990 'hg qselect [OPTION...] [GUARD...]'),
1332 "qseries":
1991 "qseries":
1333 (series,
1992 (series,
1334 [('m', 'missing', None, 'print patches not in series')],
1993 [('m', 'missing', None, 'print patches not in series'),
1994 ('s', 'summary', None, _('print first line of patch header'))],
1335 'hg qseries [-m]'),
1995 'hg qseries [-m]'),
1336 "^strip":
1996 "^strip":
1337 (strip,
1997 (strip,
@@ -1341,6 +2001,4 b' cmdtable = {'
1341 'hg strip [-f] [-b] [-n] REV'),
2001 'hg strip [-f] [-b] [-n] REV'),
1342 "qtop": (top, [], 'hg qtop'),
2002 "qtop": (top, [], 'hg qtop'),
1343 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
2003 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1344 "qversion": (version, [], 'hg qversion')
1345 }
2004 }
1346
@@ -67,8 +67,8 b''
67 from mercurial.demandload import *
67 from mercurial.demandload import *
68 from mercurial.i18n import gettext as _
68 from mercurial.i18n import gettext as _
69 from mercurial.node import *
69 from mercurial.node import *
70 demandload(globals(), 'email.Parser mercurial:commands,templater,util')
70 demandload(globals(), 'mercurial:commands,patch,templater,util,mail')
71 demandload(globals(), 'fnmatch socket time')
71 demandload(globals(), 'email.Parser fnmatch socket time')
72
72
73 # template for single changeset can include email headers.
73 # template for single changeset can include email headers.
74 single_template = '''
74 single_template = '''
@@ -229,8 +229,8 b' class notifier(object):'
229 else:
229 else:
230 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
230 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
231 (len(self.subs), count))
231 (len(self.subs), count))
232 mail = self.ui.sendmail()
232 mail.sendmail(self.ui, templater.email(msg['From']),
233 mail.sendmail(templater.email(msg['From']), self.subs, msgtext)
233 self.subs, msgtext)
234
234
235 def diff(self, node, ref):
235 def diff(self, node, ref):
236 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
236 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
@@ -238,7 +238,7 b' class notifier(object):'
238 return
238 return
239 fp = templater.stringio()
239 fp = templater.stringio()
240 prev = self.repo.changelog.parents(node)[0]
240 prev = self.repo.changelog.parents(node)[0]
241 commands.dodiff(fp, self.ui, self.repo, prev, ref)
241 patch.diff(self.repo, fp, prev, ref)
242 difflines = fp.getvalue().splitlines(1)
242 difflines = fp.getvalue().splitlines(1)
243 if maxdiff > 0 and len(difflines) > maxdiff:
243 if maxdiff > 0 and len(difflines) > maxdiff:
244 self.sio.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
244 self.sio.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
@@ -255,7 +255,7 b' def hook(ui, repo, hooktype, node=None, '
255 changegroup. else send one email per changeset.'''
255 changegroup. else send one email per changeset.'''
256 n = notifier(ui, repo, hooktype)
256 n = notifier(ui, repo, hooktype)
257 if not n.subs:
257 if not n.subs:
258 ui.debug(_('notify: no subscribers to this repo\n'))
258 ui.debug(_('notify: no subscribers to repo %s\n' % n.root))
259 return
259 return
260 if n.skipsource(source):
260 if n.skipsource(source):
261 ui.debug(_('notify: changes have source "%s" - skipping\n') %
261 ui.debug(_('notify: changes have source "%s" - skipping\n') %
@@ -23,27 +23,52 b''
23 # the changeset summary, so you can be sure you are sending the right
23 # the changeset summary, so you can be sure you are sending the right
24 # changes.
24 # changes.
25 #
25 #
26 # It is best to run this script with the "-n" (test only) flag before
26 # To enable this extension:
27 # firing it up "for real", in which case it will use your pager to
28 # display each of the messages that it would send.
29 #
27 #
30 # The "-m" (mbox) option will create an mbox file instead of sending
28 # [extensions]
31 # the messages directly. This can be reviewed e.g. with "mutt -R -f mbox",
29 # hgext.patchbomb =
32 # and finally sent with "formail -s sendmail -bm -t < mbox".
33 #
30 #
34 # To configure other defaults, add a section like this to your hgrc
31 # To configure other defaults, add a section like this to your hgrc
35 # file:
32 # file:
36 #
33 #
37 # [email]
34 # [email]
38 # from = My Name <my@email>
35 # from = My Name <my@email>
39 # to = recipient1, recipient2, ...
36 # to = recipient1, recipient2, ...
40 # cc = cc1, cc2, ...
37 # cc = cc1, cc2, ...
38 # bcc = bcc1, bcc2, ...
39 #
40 # Then you can use the "hg email" command to mail a series of changesets
41 # as a patchbomb.
42 #
43 # To avoid sending patches prematurely, it is a good idea to first run
44 # the "email" command with the "-n" option (test only). You will be
45 # prompted for an email recipient address, a subject an an introductory
46 # message describing the patches of your patchbomb. Then when all is
47 # done, your pager will be fired up once for each patchbomb message, so
48 # you can verify everything is alright.
49 #
50 # The "-m" (mbox) option is also very useful. Instead of previewing
51 # each patchbomb message in a pager or sending the messages directly,
52 # it will create a UNIX mailbox file with the patch emails. This
53 # mailbox file can be previewed with any mail user agent which supports
54 # UNIX mbox files, i.e. with mutt:
55 #
56 # % mutt -R -f mbox
57 #
58 # When you are previewing the patchbomb messages, you can use `formail'
59 # (a utility that is commonly installed as part of the procmail package),
60 # to send each message out:
61 #
62 # % formail -s sendmail -bm -t < mbox
63 #
64 # That should be all. Now your patchbomb is on its way out.
41
65
42 from mercurial.demandload import *
66 from mercurial.demandload import *
43 demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
67 demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
44 mercurial:commands,hg,ui
68 mercurial:commands,hg,mail,ui
45 os errno popen2 socket sys tempfile time''')
69 os errno popen2 socket sys tempfile time''')
46 from mercurial.i18n import gettext as _
70 from mercurial.i18n import gettext as _
71 from mercurial.node import *
47
72
48 try:
73 try:
49 # readline gives raw_input editing capabilities, but is not
74 # readline gives raw_input editing capabilities, but is not
@@ -129,8 +154,26 b' def patchbomb(ui, repo, *revs, **opts):'
129 while patch and not patch[0].strip(): patch.pop(0)
154 while patch and not patch[0].strip(): patch.pop(0)
130 if opts['diffstat']:
155 if opts['diffstat']:
131 body += cdiffstat('\n'.join(desc), patch) + '\n\n'
156 body += cdiffstat('\n'.join(desc), patch) + '\n\n'
132 body += '\n'.join(patch)
157 if opts['attach']:
133 msg = email.MIMEText.MIMEText(body)
158 msg = email.MIMEMultipart.MIMEMultipart()
159 if body: msg.attach(email.MIMEText.MIMEText(body, 'plain'))
160 p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch')
161 binnode = bin(node)
162 # if node is mq patch, it will have patch file name as tag
163 patchname = [t for t in repo.nodetags(binnode)
164 if t.endswith('.patch') or t.endswith('.diff')]
165 if patchname:
166 patchname = patchname[0]
167 elif total > 1:
168 patchname = commands.make_filename(repo, '%b-%n.patch',
169 binnode, idx, total)
170 else:
171 patchname = commands.make_filename(repo, '%b.patch', binnode)
172 p['Content-Disposition'] = 'inline; filename=' + patchname
173 msg.attach(p)
174 else:
175 body += '\n'.join(patch)
176 msg = email.MIMEText.MIMEText(body)
134 if total == 1:
177 if total == 1:
135 subj = '[PATCH] ' + desc[0].strip()
178 subj = '[PATCH] ' + desc[0].strip()
136 else:
179 else:
@@ -185,11 +228,14 b' def patchbomb(ui, repo, *revs, **opts):'
185 to = getaddrs('to', 'To')
228 to = getaddrs('to', 'To')
186 cc = getaddrs('cc', 'Cc', '')
229 cc = getaddrs('cc', 'Cc', '')
187
230
231 bcc = opts['bcc'] or (ui.config('email', 'bcc') or
232 ui.config('patchbomb', 'bcc') or '').split(',')
233 bcc = [a.strip() for a in bcc if a.strip()]
234
188 if len(patches) > 1:
235 if len(patches) > 1:
189 ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
236 ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
190
237
191 msg = email.MIMEMultipart.MIMEMultipart()
238 subj = '[PATCH 0 of %d] %s' % (
192 msg['Subject'] = '[PATCH 0 of %d] %s' % (
193 len(patches),
239 len(patches),
194 opts['subject'] or
240 opts['subject'] or
195 prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches)))
241 prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches)))
@@ -204,18 +250,21 b' def patchbomb(ui, repo, *revs, **opts):'
204 if l == '.': break
250 if l == '.': break
205 body.append(l)
251 body.append(l)
206
252
207 msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n'))
208
209 if opts['diffstat']:
253 if opts['diffstat']:
210 d = cdiffstat(_('Final summary:\n'), jumbo)
254 d = cdiffstat(_('Final summary:\n'), jumbo)
211 if d: msg.attach(email.MIMEText.MIMEText(d))
255 if d: body.append('\n' + d)
256
257 body = '\n'.join(body) + '\n'
258
259 msg = email.MIMEText.MIMEText(body)
260 msg['Subject'] = subj
212
261
213 msgs.insert(0, msg)
262 msgs.insert(0, msg)
214
263
215 ui.write('\n')
264 ui.write('\n')
216
265
217 if not opts['test'] and not opts['mbox']:
266 if not opts['test'] and not opts['mbox']:
218 mail = ui.sendmail()
267 mailer = mail.connect(ui)
219 parent = None
268 parent = None
220
269
221 # Calculate UTC offset
270 # Calculate UTC offset
@@ -240,7 +289,8 b' def patchbomb(ui, repo, *revs, **opts):'
240 start_time += 1
289 start_time += 1
241 m['From'] = sender
290 m['From'] = sender
242 m['To'] = ', '.join(to)
291 m['To'] = ', '.join(to)
243 if cc: m['Cc'] = ', '.join(cc)
292 if cc: m['Cc'] = ', '.join(cc)
293 if bcc: m['Bcc'] = ', '.join(bcc)
244 if opts['test']:
294 if opts['test']:
245 ui.status('Displaying ', m['Subject'], ' ...\n')
295 ui.status('Displaying ', m['Subject'], ' ...\n')
246 fp = os.popen(os.getenv('PAGER', 'more'), 'w')
296 fp = os.popen(os.getenv('PAGER', 'more'), 'w')
@@ -261,12 +311,16 b' def patchbomb(ui, repo, *revs, **opts):'
261 fp.close()
311 fp.close()
262 else:
312 else:
263 ui.status('Sending ', m['Subject'], ' ...\n')
313 ui.status('Sending ', m['Subject'], ' ...\n')
264 mail.sendmail(sender, to + cc, m.as_string(0))
314 # Exim does not remove the Bcc field
315 del m['Bcc']
316 mailer.sendmail(sender, to + bcc + cc, m.as_string(0))
265
317
266 cmdtable = {
318 cmdtable = {
267 'email':
319 'email':
268 (patchbomb,
320 (patchbomb,
269 [('c', 'cc', [], 'email addresses of copy recipients'),
321 [('a', 'attach', None, 'send patches as inline attachments'),
322 ('', 'bcc', [], 'email addresses of blind copy recipients'),
323 ('c', 'cc', [], 'email addresses of copy recipients'),
270 ('d', 'diffstat', None, 'add diffstat output to messages'),
324 ('d', 'diffstat', None, 'add diffstat output to messages'),
271 ('f', 'from', '', 'email address of sender'),
325 ('f', 'from', '', 'email address of sender'),
272 ('', 'plain', None, 'omit hg patch header'),
326 ('', 'plain', None, 'omit hg patch header'),
@@ -163,12 +163,12 b' def archive(repo, dest, node, kind, deco'
163 change = repo.changelog.read(node)
163 change = repo.changelog.read(node)
164 mn = change[0]
164 mn = change[0]
165 archiver = archivers[kind](dest, prefix, mtime or change[2][0])
165 archiver = archivers[kind](dest, prefix, mtime or change[2][0])
166 mf = repo.manifest.read(mn).items()
166 m = repo.manifest.read(mn)
167 mff = repo.manifest.readflags(mn)
167 items = m.items()
168 mf.sort()
168 items.sort()
169 write('.hg_archival.txt', 0644,
169 write('.hg_archival.txt', 0644,
170 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
170 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
171 for filename, filenode in mf:
171 for filename, filenode in items:
172 write(filename, mff[filename] and 0755 or 0644,
172 write(filename, m.execf(filename) and 0755 or 0644,
173 repo.file(filename).read(filenode))
173 repo.file(filename).read(filenode))
174 archiver.done()
174 archiver.done()
@@ -1,7 +1,7 b''
1 /*
1 /*
2 bdiff.c - efficient binary diff extension for Mercurial
2 bdiff.c - efficient binary diff extension for Mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
@@ -159,6 +159,10 b' class bundlefilelog(bundlerevlog, filelo'
159 class bundlerepository(localrepo.localrepository):
159 class bundlerepository(localrepo.localrepository):
160 def __init__(self, ui, path, bundlename):
160 def __init__(self, ui, path, bundlename):
161 localrepo.localrepository.__init__(self, ui, path)
161 localrepo.localrepository.__init__(self, ui, path)
162
163 self._url = 'bundle:' + bundlename
164 if path: self._url += '+' + path
165
162 self.tempfile = None
166 self.tempfile = None
163 self.bundlefile = open(bundlename, "rb")
167 self.bundlefile = open(bundlename, "rb")
164 header = self.bundlefile.read(6)
168 header = self.bundlefile.read(6)
@@ -208,6 +212,9 b' class bundlerepository(localrepo.localre'
208 for c in changegroup.chunkiter(self.bundlefile):
212 for c in changegroup.chunkiter(self.bundlefile):
209 pass
213 pass
210
214
215 def url(self):
216 return self._url
217
211 def dev(self):
218 def dev(self):
212 return -1
219 return -1
213
220
@@ -230,3 +237,18 b' class bundlerepository(localrepo.localre'
230 self.bundlefile.close()
237 self.bundlefile.close()
231 if self.tempfile is not None:
238 if self.tempfile is not None:
232 os.unlink(self.tempfile)
239 os.unlink(self.tempfile)
240
241 def instance(ui, path, create):
242 if create:
243 raise util.Abort(_('cannot create new bundle repository'))
244 path = util.drop_scheme('file', path)
245 if path.startswith('bundle:'):
246 path = util.drop_scheme('bundle', path)
247 s = path.split("+", 1)
248 if len(s) == 1:
249 repopath, bundlename = "", s[0]
250 else:
251 repopath, bundlename = s
252 else:
253 repopath, bundlename = '', path
254 return bundlerepository(ui, repopath, bundlename)
@@ -1,6 +1,6 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class for mercurial
2 #
2 #
3 # Copyright 2005 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.
This diff has been collapsed as it changes many lines, (868 lines changed) Show them Hide them
@@ -1,6 +1,6 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -10,10 +10,10 b' 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 mdiff 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 email.Parser")
15 demandload(globals(), "archival cStringIO changegroup")
16 demandload(globals(), "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."""
@@ -21,47 +21,34 b' 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, unknown = repo.changes()
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 filterfiles(filters, files):
29 l = [x for x in files if x in filters]
30
31 for t in filters:
32 if t and t[-1] != "/":
33 t += "/"
34 l += [x for x in files if x.startswith(t)]
35 return l
36
37 def relpath(repo, args):
28 def relpath(repo, args):
38 cwd = repo.getcwd()
29 cwd = repo.getcwd()
39 if cwd:
30 if cwd:
40 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]
41 return args
32 return args
42
33
43 def matchpats(repo, pats=[], opts={}, head=''):
34 def logmessage(opts):
44 cwd = repo.getcwd()
35 """ get the log message according to -m and -l option """
45 if not pats and cwd:
36 message = opts['message']
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
37 logfile = opts['logfile']
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
38
48 cwd = ''
39 if message and logfile:
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
40 raise util.Abort(_('options --message and --logfile are mutually '
50 opts.get('exclude'), head)
41 'exclusive'))
51
42 if not message and logfile:
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
43 try:
53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
44 if logfile == '-':
54 exact = dict(zip(files, files))
45 message = sys.stdin.read()
55 def walk():
46 else:
56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
47 message = open(logfile).read()
57 badmatch=badmatch):
48 except IOError, inst:
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
49 raise util.Abort(_("can't read commit message '%s': %s") %
59 return files, matchfn, walk()
50 (logfile, inst.strerror))
60
51 return message
61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
63 for r in results:
64 yield r
65
52
66 def walkchangerevs(ui, repo, pats, opts):
53 def walkchangerevs(ui, repo, pats, opts):
67 '''Iterate over files and the revs they changed in.
54 '''Iterate over files and the revs they changed in.
@@ -105,12 +92,23 b' def walkchangerevs(ui, repo, pats, opts)'
105 windowsize *= 2
92 windowsize *= 2
106
93
107
94
108 files, matchfn, anypats = matchpats(repo, pats, opts)
95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 follow = opts.get('follow') or opts.get('follow_first')
109
97
110 if repo.changelog.count() == 0:
98 if repo.changelog.count() == 0:
111 return [], False, matchfn
99 return [], False, matchfn
112
100
113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
101 if follow:
102 p = repo.dirstate.parents()[0]
103 if p == nullid:
104 ui.warn(_('No working directory revision; defaulting to tip\n'))
105 start = 'tip'
106 else:
107 start = repo.changelog.rev(p)
108 defrange = '%s:0' % start
109 else:
110 defrange = 'tip:0'
111 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
114 wanted = {}
112 wanted = {}
115 slowpath = anypats
113 slowpath = anypats
116 fncache = {}
114 fncache = {}
@@ -125,37 +123,54 b' def walkchangerevs(ui, repo, pats, opts)'
125 if not slowpath and not files:
123 if not slowpath and not files:
126 # No files, no patterns. Display all revs.
124 # No files, no patterns. Display all revs.
127 wanted = dict(zip(revs, revs))
125 wanted = dict(zip(revs, revs))
126 copies = []
128 if not slowpath:
127 if not slowpath:
129 # Only files, no patterns. Check the history of each file.
128 # Only files, no patterns. Check the history of each file.
130 def filerevgen(filelog):
129 def filerevgen(filelog, node):
131 cl_count = repo.changelog.count()
130 cl_count = repo.changelog.count()
132 for i, window in increasing_windows(filelog.count()-1, -1):
131 if node is None:
132 last = filelog.count() - 1
133 else:
134 last = filelog.rev(node)
135 for i, window in increasing_windows(last, -1):
133 revs = []
136 revs = []
134 for j in xrange(i - window, i + 1):
137 for j in xrange(i - window, i + 1):
135 revs.append(filelog.linkrev(filelog.node(j)))
138 n = filelog.node(j)
139 revs.append((filelog.linkrev(n),
140 follow and filelog.renamed(n)))
136 revs.reverse()
141 revs.reverse()
137 for rev in revs:
142 for rev in revs:
138 # only yield rev for which we have the changelog, it can
143 # only yield rev for which we have the changelog, it can
139 # happen while doing "hg log" during a pull or commit
144 # happen while doing "hg log" during a pull or commit
140 if rev < cl_count:
145 if rev[0] < cl_count:
141 yield rev
146 yield rev
142
147 def iterfiles():
148 for filename in files:
149 yield filename, None
150 for filename_node in copies:
151 yield filename_node
143 minrev, maxrev = min(revs), max(revs)
152 minrev, maxrev = min(revs), max(revs)
144 for file_ in files:
153 for file_, node in iterfiles():
145 filelog = repo.file(file_)
154 filelog = repo.file(file_)
146 # A zero count may be a directory or deleted file, so
155 # A zero count may be a directory or deleted file, so
147 # try to find matching entries on the slow path.
156 # try to find matching entries on the slow path.
148 if filelog.count() == 0:
157 if filelog.count() == 0:
149 slowpath = True
158 slowpath = True
150 break
159 break
151 for rev in filerevgen(filelog):
160 for rev, copied in filerevgen(filelog, node):
152 if rev <= maxrev:
161 if rev <= maxrev:
153 if rev < minrev:
162 if rev < minrev:
154 break
163 break
155 fncache.setdefault(rev, [])
164 fncache.setdefault(rev, [])
156 fncache[rev].append(file_)
165 fncache[rev].append(file_)
157 wanted[rev] = 1
166 wanted[rev] = 1
167 if follow and copied:
168 copies.append(copied)
158 if slowpath:
169 if slowpath:
170 if follow:
171 raise util.Abort(_('can only follow copies/renames for explicit '
172 'file names'))
173
159 # The slow path checks files modified in every changeset.
174 # The slow path checks files modified in every changeset.
160 def changerevgen():
175 def changerevgen():
161 for i, window in increasing_windows(repo.changelog.count()-1, -1):
176 for i, window in increasing_windows(repo.changelog.count()-1, -1):
@@ -168,11 +183,66 b' def walkchangerevs(ui, repo, pats, opts)'
168 fncache[rev] = matches
183 fncache[rev] = matches
169 wanted[rev] = 1
184 wanted[rev] = 1
170
185
186 class followfilter:
187 def __init__(self, onlyfirst=False):
188 self.startrev = -1
189 self.roots = []
190 self.onlyfirst = onlyfirst
191
192 def match(self, rev):
193 def realparents(rev):
194 if self.onlyfirst:
195 return repo.changelog.parentrevs(rev)[0:1]
196 else:
197 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
198
199 if self.startrev == -1:
200 self.startrev = rev
201 return True
202
203 if rev > self.startrev:
204 # forward: all descendants
205 if not self.roots:
206 self.roots.append(self.startrev)
207 for parent in realparents(rev):
208 if parent in self.roots:
209 self.roots.append(rev)
210 return True
211 else:
212 # backwards: all parents
213 if not self.roots:
214 self.roots.extend(realparents(self.startrev))
215 if rev in self.roots:
216 self.roots.remove(rev)
217 self.roots.extend(realparents(rev))
218 return True
219
220 return False
221
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
224 for rev in opts.get('prune', ()):
225 rev = repo.changelog.rev(repo.lookup(rev))
226 ff = followfilter()
227 stop = min(revs[0], revs[-1])
228 for x in range(rev, stop-1, -1):
229 if ff.match(x) and wanted.has_key(x):
230 del wanted[x]
231
171 def iterate():
232 def iterate():
233 if follow and not files:
234 ff = followfilter(onlyfirst=opts.get('follow_first'))
235 def want(rev):
236 if ff.match(rev) and rev in wanted:
237 return True
238 return False
239 else:
240 def want(rev):
241 return rev in wanted
242
172 for i, window in increasing_windows(0, len(revs)):
243 for i, window in increasing_windows(0, len(revs)):
173 yield 'window', revs[0] < revs[-1], revs[-1]
244 yield 'window', revs[0] < revs[-1], revs[-1]
174 nrevs = [rev for rev in revs[i:i+window]
245 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
175 if rev in wanted]
176 srevs = list(nrevs)
246 srevs = list(nrevs)
177 srevs.sort()
247 srevs.sort()
178 for rev in srevs:
248 for rev in srevs:
@@ -252,62 +322,6 b' def revrange(ui, repo, revs):'
252 seen[rev] = 1
322 seen[rev] = 1
253 yield str(rev)
323 yield str(rev)
254
324
255 def make_filename(repo, pat, node,
256 total=None, seqno=None, revwidth=None, pathname=None):
257 node_expander = {
258 'H': lambda: hex(node),
259 'R': lambda: str(repo.changelog.rev(node)),
260 'h': lambda: short(node),
261 }
262 expander = {
263 '%': lambda: '%',
264 'b': lambda: os.path.basename(repo.root),
265 }
266
267 try:
268 if node:
269 expander.update(node_expander)
270 if node and revwidth is not None:
271 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
272 if total is not None:
273 expander['N'] = lambda: str(total)
274 if seqno is not None:
275 expander['n'] = lambda: str(seqno)
276 if total is not None and seqno is not None:
277 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
278 if pathname is not None:
279 expander['s'] = lambda: os.path.basename(pathname)
280 expander['d'] = lambda: os.path.dirname(pathname) or '.'
281 expander['p'] = lambda: pathname
282
283 newname = []
284 patlen = len(pat)
285 i = 0
286 while i < patlen:
287 c = pat[i]
288 if c == '%':
289 i += 1
290 c = pat[i]
291 c = expander[c]()
292 newname.append(c)
293 i += 1
294 return ''.join(newname)
295 except KeyError, inst:
296 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
297 inst.args[0])
298
299 def make_file(repo, pat, node=None,
300 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
301 if not pat or pat == '-':
302 return 'w' in mode and sys.stdout or sys.stdin
303 if hasattr(pat, 'write') and 'w' in mode:
304 return pat
305 if hasattr(pat, 'read') and 'r' in mode:
306 return pat
307 return open(make_filename(repo, pat, node, total, seqno, revwidth,
308 pathname),
309 mode)
310
311 def write_bundle(cg, filename=None, compress=True):
325 def write_bundle(cg, filename=None, compress=True):
312 """Write a bundle file and return its filename.
326 """Write a bundle file and return its filename.
313
327
@@ -360,83 +374,6 b' def write_bundle(cg, filename=None, comp'
360 if cleanup is not None:
374 if cleanup is not None:
361 os.unlink(cleanup)
375 os.unlink(cleanup)
362
376
363 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
364 changes=None, text=False, opts={}):
365 if not node1:
366 node1 = repo.dirstate.parents()[0]
367 # reading the data for node1 early allows it to play nicely
368 # with repo.changes and the revlog cache.
369 change = repo.changelog.read(node1)
370 mmap = repo.manifest.read(change[0])
371 date1 = util.datestr(change[2])
372
373 if not changes:
374 changes = repo.changes(node1, node2, files, match=match)
375 modified, added, removed, deleted, unknown = changes
376 if files:
377 modified, added, removed = map(lambda x: filterfiles(files, x),
378 (modified, added, removed))
379
380 if not modified and not added and not removed:
381 return
382
383 if node2:
384 change = repo.changelog.read(node2)
385 mmap2 = repo.manifest.read(change[0])
386 _date2 = util.datestr(change[2])
387 def date2(f):
388 return _date2
389 def read(f):
390 return repo.file(f).read(mmap2[f])
391 else:
392 tz = util.makedate()[1]
393 _date2 = util.datestr()
394 def date2(f):
395 try:
396 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
397 except OSError, err:
398 if err.errno != errno.ENOENT: raise
399 return _date2
400 def read(f):
401 return repo.wread(f)
402
403 if ui.quiet:
404 r = None
405 else:
406 hexfunc = ui.verbose and hex or short
407 r = [hexfunc(node) for node in [node1, node2] if node]
408
409 diffopts = ui.diffopts()
410 showfunc = opts.get('show_function') or diffopts['showfunc']
411 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
412 ignorewsamount = opts.get('ignore_space_change') or \
413 diffopts['ignorewsamount']
414 ignoreblanklines = opts.get('ignore_blank_lines') or \
415 diffopts['ignoreblanklines']
416 for f in modified:
417 to = None
418 if f in mmap:
419 to = repo.file(f).read(mmap[f])
420 tn = read(f)
421 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
422 showfunc=showfunc, ignorews=ignorews,
423 ignorewsamount=ignorewsamount,
424 ignoreblanklines=ignoreblanklines))
425 for f in added:
426 to = None
427 tn = read(f)
428 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
429 showfunc=showfunc, ignorews=ignorews,
430 ignorewsamount=ignorewsamount,
431 ignoreblanklines=ignoreblanklines))
432 for f in removed:
433 to = repo.file(f).read(mmap[f])
434 tn = None
435 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
436 showfunc=showfunc, ignorews=ignorews,
437 ignorewsamount=ignorewsamount,
438 ignoreblanklines=ignoreblanklines))
439
440 def trimuser(ui, name, rev, revcache):
377 def trimuser(ui, name, rev, revcache):
441 """trim the name of the user who committed a change"""
378 """trim the name of the user who committed a change"""
442 user = revcache.get(rev)
379 user = revcache.get(rev)
@@ -493,7 +430,7 b' class changeset_printer(object):'
493 self.ui.status(_("date: %s\n") % date)
430 self.ui.status(_("date: %s\n") % date)
494
431
495 if self.ui.debugflag:
432 if self.ui.debugflag:
496 files = self.repo.changes(log.parents(changenode)[0], changenode)
433 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
497 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
434 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
498 files):
435 files):
499 if value:
436 if value:
@@ -537,12 +474,19 b' def show_changeset(ui, repo, opts):'
537 return t
474 return t
538 return changeset_printer(ui, repo)
475 return changeset_printer(ui, repo)
539
476
477 def setremoteconfig(ui, opts):
478 "copy remote options to ui tree"
479 if opts.get('ssh'):
480 ui.setconfig("ui", "ssh", opts['ssh'])
481 if opts.get('remotecmd'):
482 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
483
540 def show_version(ui):
484 def show_version(ui):
541 """output version and copyright information"""
485 """output version and copyright information"""
542 ui.write(_("Mercurial Distributed SCM (version %s)\n")
486 ui.write(_("Mercurial Distributed SCM (version %s)\n")
543 % version.get_version())
487 % version.get_version())
544 ui.status(_(
488 ui.status(_(
545 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
489 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
546 "This is free software; see the source for copying conditions. "
490 "This is free software; see the source for copying conditions. "
547 "There is NO\nwarranty; "
491 "There is NO\nwarranty; "
548 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
492 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
@@ -696,7 +640,7 b' def add(ui, repo, *pats, **opts):'
696 """
640 """
697
641
698 names = []
642 names = []
699 for src, abs, rel, exact in walk(repo, pats, opts):
643 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
700 if exact:
644 if exact:
701 if ui.verbose:
645 if ui.verbose:
702 ui.status(_('adding %s\n') % rel)
646 ui.status(_('adding %s\n') % rel)
@@ -715,22 +659,7 b' def addremove(ui, repo, *pats, **opts):'
715 New files are ignored if they match any of the patterns in .hgignore. As
659 New files are ignored if they match any of the patterns in .hgignore. As
716 with add, these changes take effect at the next commit.
660 with add, these changes take effect at the next commit.
717 """
661 """
718 return addremove_lock(ui, repo, pats, opts)
662 return cmdutil.addremove(repo, pats, opts)
719
720 def addremove_lock(ui, repo, pats, opts, wlock=None):
721 add, remove = [], []
722 for src, abs, rel, exact in walk(repo, pats, opts):
723 if src == 'f' and repo.dirstate.state(abs) == '?':
724 add.append(abs)
725 if ui.verbose or not exact:
726 ui.status(_('adding %s\n') % ((pats and rel) or abs))
727 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
728 remove.append(abs)
729 if ui.verbose or not exact:
730 ui.status(_('removing %s\n') % ((pats and rel) or abs))
731 if not opts.get('dry_run'):
732 repo.add(add, wlock=wlock)
733 repo.remove(remove, wlock=wlock)
734
663
735 def annotate(ui, repo, *pats, **opts):
664 def annotate(ui, repo, *pats, **opts):
736 """show changeset information per file line
665 """show changeset information per file line
@@ -773,7 +702,8 b' def annotate(ui, repo, *pats, **opts):'
773
702
774 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
703 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
775
704
776 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
705 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
706 node=ctx.node()):
777 fctx = ctx.filectx(abs)
707 fctx = ctx.filectx(abs)
778 if not opts['text'] and util.binary(fctx.data()):
708 if not opts['text'] and util.binary(fctx.data()):
779 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
709 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
@@ -825,10 +755,10 b' def archive(ui, repo, dest, **opts):'
825 raise util.Abort(_('uncommitted merge - please provide a '
755 raise util.Abort(_('uncommitted merge - please provide a '
826 'specific revision'))
756 'specific revision'))
827
757
828 dest = make_filename(repo, dest, node)
758 dest = cmdutil.make_filename(repo, dest, node)
829 if os.path.realpath(dest) == repo.root:
759 if os.path.realpath(dest) == repo.root:
830 raise util.Abort(_('repository root cannot be destination'))
760 raise util.Abort(_('repository root cannot be destination'))
831 dummy, matchfn, dummy = matchpats(repo, [], opts)
761 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
832 kind = opts.get('type') or 'files'
762 kind = opts.get('type') or 'files'
833 prefix = opts['prefix']
763 prefix = opts['prefix']
834 if dest == '-':
764 if dest == '-':
@@ -836,7 +766,7 b' def archive(ui, repo, dest, **opts):'
836 raise util.Abort(_('cannot archive plain files to stdout'))
766 raise util.Abort(_('cannot archive plain files to stdout'))
837 dest = sys.stdout
767 dest = sys.stdout
838 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
768 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
839 prefix = make_filename(repo, prefix, node)
769 prefix = cmdutil.make_filename(repo, prefix, node)
840 archival.archive(repo, dest, node, kind, not opts['no_decode'],
770 archival.archive(repo, dest, node, kind, not opts['no_decode'],
841 matchfn, prefix)
771 matchfn, prefix)
842
772
@@ -879,7 +809,7 b' def backout(ui, repo, rev, **opts):'
879 if opts['parent']:
809 if opts['parent']:
880 raise util.Abort(_('cannot use --parent on non-merge changeset'))
810 raise util.Abort(_('cannot use --parent on non-merge changeset'))
881 parent = p1
811 parent = p1
882 repo.update(node, force=True, show_stats=False)
812 hg.clean(repo, node, show_stats=False)
883 revert_opts = opts.copy()
813 revert_opts = opts.copy()
884 revert_opts['rev'] = hex(parent)
814 revert_opts['rev'] = hex(parent)
885 revert(ui, repo, **revert_opts)
815 revert(ui, repo, **revert_opts)
@@ -896,11 +826,13 b' def backout(ui, repo, rev, **opts):'
896 if op1 != node:
826 if op1 != node:
897 if opts['merge']:
827 if opts['merge']:
898 ui.status(_('merging with changeset %s\n') % nice(op1))
828 ui.status(_('merging with changeset %s\n') % nice(op1))
899 doupdate(ui, repo, hex(op1), **opts)
829 n = _lookup(repo, hex(op1))
830 hg.merge(repo, n)
900 else:
831 else:
901 ui.status(_('the backout changeset is a new head - '
832 ui.status(_('the backout changeset is a new head - '
902 'do not forget to merge\n'))
833 'do not forget to merge\n'))
903 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
834 ui.status(_('(use "backout --merge" '
835 'if you want to auto-merge)\n'))
904
836
905 def bundle(ui, repo, fname, dest=None, **opts):
837 def bundle(ui, repo, fname, dest=None, **opts):
906 """create a changegroup file
838 """create a changegroup file
@@ -937,9 +869,10 b' def cat(ui, repo, file1, *pats, **opts):'
937 %d dirname of file being printed, or '.' if in repo root
869 %d dirname of file being printed, or '.' if in repo root
938 %p root-relative path name of file being printed
870 %p root-relative path name of file being printed
939 """
871 """
940 ctx = repo.changectx(opts['rev'] or -1)
872 ctx = repo.changectx(opts['rev'] or "-1")
941 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
873 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
942 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
874 ctx.node()):
875 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
943 fp.write(ctx.filectx(abs).data())
876 fp.write(ctx.filectx(abs).data())
944
877
945 def clone(ui, source, dest=None, **opts):
878 def clone(ui, source, dest=None, **opts):
@@ -954,10 +887,25 b' def clone(ui, source, dest=None, **opts)'
954 .hg/hgrc file, as the default to be used for future pulls.
887 .hg/hgrc file, as the default to be used for future pulls.
955
888
956 For efficiency, hardlinks are used for cloning whenever the source
889 For efficiency, hardlinks are used for cloning whenever the source
957 and destination are on the same filesystem. Some filesystems,
890 and destination are on the same filesystem (note this applies only
958 such as AFS, implement hardlinking incorrectly, but do not report
891 to the repository data, not to the checked out files). Some
959 errors. In these cases, use the --pull option to avoid
892 filesystems, such as AFS, implement hardlinking incorrectly, but
960 hardlinking.
893 do not report errors. In these cases, use the --pull option to
894 avoid hardlinking.
895
896 You can safely clone repositories and checked out files using full
897 hardlinks with
898
899 $ cp -al REPO REPOCLONE
900
901 which is the fastest way to clone. However, the operation is not
902 atomic (making sure REPO is not modified during the operation is
903 up to you) and you have to make sure your editor breaks hardlinks
904 (Emacs and most Linux Kernel tools do so).
905
906 If you use the -r option to clone up to a specific revision, no
907 subsequent revisions will be present in the cloned repository.
908 This option implies --pull, even on local repositories.
961
909
962 See pull for valid source format details.
910 See pull for valid source format details.
963
911
@@ -965,7 +913,7 b' def clone(ui, source, dest=None, **opts)'
965 .hg/hgrc will be created on the remote side. Look at the help text
913 .hg/hgrc will be created on the remote side. Look at the help text
966 for the pull command for important details about ssh:// URLs.
914 for the pull command for important details about ssh:// URLs.
967 """
915 """
968 ui.setconfig_remoteopts(**opts)
916 setremoteconfig(ui, opts)
969 hg.clone(ui, ui.expandpath(source), dest,
917 hg.clone(ui, ui.expandpath(source), dest,
970 pull=opts['pull'],
918 pull=opts['pull'],
971 stream=opts['uncompressed'],
919 stream=opts['uncompressed'],
@@ -983,28 +931,13 b' def commit(ui, repo, *pats, **opts):'
983 If no commit message is specified, the editor configured in your hgrc
931 If no commit message is specified, the editor configured in your hgrc
984 or in the EDITOR environment variable is started to enter a message.
932 or in the EDITOR environment variable is started to enter a message.
985 """
933 """
986 message = opts['message']
934 message = logmessage(opts)
987 logfile = opts['logfile']
988
989 if message and logfile:
990 raise util.Abort(_('options --message and --logfile are mutually '
991 'exclusive'))
992 if not message and logfile:
993 try:
994 if logfile == '-':
995 message = sys.stdin.read()
996 else:
997 message = open(logfile).read()
998 except IOError, inst:
999 raise util.Abort(_("can't read commit message '%s': %s") %
1000 (logfile, inst.strerror))
1001
935
1002 if opts['addremove']:
936 if opts['addremove']:
1003 addremove_lock(ui, repo, pats, opts)
937 cmdutil.addremove(repo, pats, opts)
1004 fns, match, anypats = matchpats(repo, pats, opts)
938 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
1005 if pats:
939 if pats:
1006 modified, added, removed, deleted, unknown = (
940 modified, added, removed = repo.status(files=fns, match=match)[:3]
1007 repo.changes(files=fns, match=match))
1008 files = modified + added + removed
941 files = modified + added + removed
1009 else:
942 else:
1010 files = []
943 files = []
@@ -1159,7 +1092,7 b' def docopy(ui, repo, pats, opts, wlock):'
1159 copylist = []
1092 copylist = []
1160 for pat in pats:
1093 for pat in pats:
1161 srcs = []
1094 srcs = []
1162 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1095 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1163 origsrc = okaytocopy(abssrc, relsrc, exact)
1096 origsrc = okaytocopy(abssrc, relsrc, exact)
1164 if origsrc:
1097 if origsrc:
1165 srcs.append((origsrc, abssrc, relsrc, exact))
1098 srcs.append((origsrc, abssrc, relsrc, exact))
@@ -1233,9 +1166,9 b' def debugrebuildstate(ui, repo, rev=None'
1233 rev = repo.lookup(rev)
1166 rev = repo.lookup(rev)
1234 change = repo.changelog.read(rev)
1167 change = repo.changelog.read(rev)
1235 n = change[0]
1168 n = change[0]
1236 files = repo.manifest.readflags(n)
1169 files = repo.manifest.read(n)
1237 wlock = repo.wlock()
1170 wlock = repo.wlock()
1238 repo.dirstate.rebuild(rev, files.iteritems())
1171 repo.dirstate.rebuild(rev, files)
1239
1172
1240 def debugcheckstate(ui, repo):
1173 def debugcheckstate(ui, repo):
1241 """validate the correctness of the current dirstate"""
1174 """validate the correctness of the current dirstate"""
@@ -1376,7 +1309,7 b' def debugrename(ui, repo, file, rev=None'
1376
1309
1377 def debugwalk(ui, repo, *pats, **opts):
1310 def debugwalk(ui, repo, *pats, **opts):
1378 """show how files match on given patterns"""
1311 """show how files match on given patterns"""
1379 items = list(walk(repo, pats, opts))
1312 items = list(cmdutil.walk(repo, pats, opts))
1380 if not items:
1313 if not items:
1381 return
1314 return
1382 fmt = '%%s %%-%ds %%-%ds %%s' % (
1315 fmt = '%%s %%-%ds %%-%ds %%s' % (
@@ -1405,37 +1338,10 b' def diff(ui, repo, *pats, **opts):'
1405 """
1338 """
1406 node1, node2 = revpair(ui, repo, opts['rev'])
1339 node1, node2 = revpair(ui, repo, opts['rev'])
1407
1340
1408 fns, matchfn, anypats = matchpats(repo, pats, opts)
1341 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1409
1342
1410 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1343 patch.diff(repo, node1, node2, fns, match=matchfn,
1411 text=opts['text'], opts=opts)
1344 opts=patch.diffopts(ui, opts))
1412
1413 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1414 node = repo.lookup(changeset)
1415 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1416 if opts['switch_parent']:
1417 parents.reverse()
1418 prev = (parents and parents[0]) or nullid
1419 change = repo.changelog.read(node)
1420
1421 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1422 revwidth=revwidth)
1423 if fp != sys.stdout:
1424 ui.note("%s\n" % fp.name)
1425
1426 fp.write("# HG changeset patch\n")
1427 fp.write("# User %s\n" % change[1])
1428 fp.write("# Date %d %d\n" % change[2])
1429 fp.write("# Node ID %s\n" % hex(node))
1430 fp.write("# Parent %s\n" % hex(prev))
1431 if len(parents) > 1:
1432 fp.write("# Parent %s\n" % hex(parents[1]))
1433 fp.write(change[4].rstrip())
1434 fp.write("\n\n")
1435
1436 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1437 if fp != sys.stdout:
1438 fp.close()
1439
1345
1440 def export(ui, repo, *changesets, **opts):
1346 def export(ui, repo, *changesets, **opts):
1441 """dump the header and diffs for one or more changesets
1347 """dump the header and diffs for one or more changesets
@@ -1466,15 +1372,14 b' def export(ui, repo, *changesets, **opts'
1466 """
1372 """
1467 if not changesets:
1373 if not changesets:
1468 raise util.Abort(_("export requires at least one changeset"))
1374 raise util.Abort(_("export requires at least one changeset"))
1469 seqno = 0
1470 revs = list(revrange(ui, repo, changesets))
1375 revs = list(revrange(ui, repo, changesets))
1471 total = len(revs)
1376 if len(revs) > 1:
1472 revwidth = max(map(len, revs))
1377 ui.note(_('exporting patches:\n'))
1473 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1378 else:
1474 ui.note(msg)
1379 ui.note(_('exporting patch:\n'))
1475 for cset in revs:
1380 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1476 seqno += 1
1381 switch_parent=opts['switch_parent'],
1477 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1382 opts=patch.diffopts(ui, opts))
1478
1383
1479 def forget(ui, repo, *pats, **opts):
1384 def forget(ui, repo, *pats, **opts):
1480 """don't add the specified files on the next commit (DEPRECATED)
1385 """don't add the specified files on the next commit (DEPRECATED)
@@ -1487,7 +1392,7 b' def forget(ui, repo, *pats, **opts):'
1487 """
1392 """
1488 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1393 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1489 forget = []
1394 forget = []
1490 for src, abs, rel, exact in walk(repo, pats, opts):
1395 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1491 if repo.dirstate.state(abs) == 'a':
1396 if repo.dirstate.state(abs) == 'a':
1492 forget.append(abs)
1397 forget.append(abs)
1493 if ui.verbose or not exact:
1398 if ui.verbose or not exact:
@@ -1544,42 +1449,56 b' def grep(ui, repo, pattern, *pats, **opt'
1544 self.linenum = linenum
1449 self.linenum = linenum
1545 self.colstart = colstart
1450 self.colstart = colstart
1546 self.colend = colend
1451 self.colend = colend
1452
1547 def __eq__(self, other):
1453 def __eq__(self, other):
1548 return self.line == other.line
1454 return self.line == other.line
1549 def __hash__(self):
1550 return hash(self.line)
1551
1455
1552 matches = {}
1456 matches = {}
1457 copies = {}
1553 def grepbody(fn, rev, body):
1458 def grepbody(fn, rev, body):
1554 matches[rev].setdefault(fn, {})
1459 matches[rev].setdefault(fn, [])
1555 m = matches[rev][fn]
1460 m = matches[rev][fn]
1556 for lnum, cstart, cend, line in matchlines(body):
1461 for lnum, cstart, cend, line in matchlines(body):
1557 s = linestate(line, lnum, cstart, cend)
1462 s = linestate(line, lnum, cstart, cend)
1558 m[s] = s
1463 m.append(s)
1559
1464
1560 # FIXME: prev isn't used, why ?
1465 def difflinestates(a, b):
1466 sm = difflib.SequenceMatcher(None, a, b)
1467 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1468 if tag == 'insert':
1469 for i in range(blo, bhi):
1470 yield ('+', b[i])
1471 elif tag == 'delete':
1472 for i in range(alo, ahi):
1473 yield ('-', a[i])
1474 elif tag == 'replace':
1475 for i in range(alo, ahi):
1476 yield ('-', a[i])
1477 for i in range(blo, bhi):
1478 yield ('+', b[i])
1479
1561 prev = {}
1480 prev = {}
1562 ucache = {}
1481 ucache = {}
1563 def display(fn, rev, states, prevstates):
1482 def display(fn, rev, states, prevstates):
1564 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1565 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1566 counts = {'-': 0, '+': 0}
1483 counts = {'-': 0, '+': 0}
1567 filerevmatches = {}
1484 filerevmatches = {}
1568 for l in diff:
1485 if incrementing or not opts['all']:
1486 a, b = prevstates, states
1487 else:
1488 a, b = states, prevstates
1489 for change, l in difflinestates(a, b):
1569 if incrementing or not opts['all']:
1490 if incrementing or not opts['all']:
1570 change = ((l in prevstates) and '-') or '+'
1571 r = rev
1491 r = rev
1572 else:
1492 else:
1573 change = ((l in states) and '-') or '+'
1574 r = prev[fn]
1493 r = prev[fn]
1575 cols = [fn, str(rev)]
1494 cols = [fn, str(r)]
1576 if opts['line_number']:
1495 if opts['line_number']:
1577 cols.append(str(l.linenum))
1496 cols.append(str(l.linenum))
1578 if opts['all']:
1497 if opts['all']:
1579 cols.append(change)
1498 cols.append(change)
1580 if opts['user']:
1499 if opts['user']:
1581 cols.append(trimuser(ui, getchange(rev)[1], rev,
1500 cols.append(trimuser(ui, getchange(r)[1], rev,
1582 ucache))
1501 ucache))
1583 if opts['files_with_matches']:
1502 if opts['files_with_matches']:
1584 c = (fn, rev)
1503 c = (fn, rev)
1585 if c in filerevmatches:
1504 if c in filerevmatches:
@@ -1596,6 +1515,7 b' def grep(ui, repo, pattern, *pats, **opt'
1596 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1515 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1597 count = 0
1516 count = 0
1598 incrementing = False
1517 incrementing = False
1518 follow = opts.get('follow')
1599 for st, rev, fns in changeiter:
1519 for st, rev, fns in changeiter:
1600 if st == 'window':
1520 if st == 'window':
1601 incrementing = rev
1521 incrementing = rev
@@ -1610,20 +1530,31 b' def grep(ui, repo, pattern, *pats, **opt'
1610 fstate.setdefault(fn, {})
1530 fstate.setdefault(fn, {})
1611 try:
1531 try:
1612 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1532 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1533 if follow:
1534 copied = getfile(fn).renamed(mf[fn])
1535 if copied:
1536 copies.setdefault(rev, {})[fn] = copied[0]
1613 except KeyError:
1537 except KeyError:
1614 pass
1538 pass
1615 elif st == 'iter':
1539 elif st == 'iter':
1616 states = matches[rev].items()
1540 states = matches[rev].items()
1617 states.sort()
1541 states.sort()
1618 for fn, m in states:
1542 for fn, m in states:
1543 copy = copies.get(rev, {}).get(fn)
1619 if fn in skip:
1544 if fn in skip:
1545 if copy:
1546 skip[copy] = True
1620 continue
1547 continue
1621 if incrementing or not opts['all'] or fstate[fn]:
1548 if incrementing or not opts['all'] or fstate[fn]:
1622 pos, neg = display(fn, rev, m, fstate[fn])
1549 pos, neg = display(fn, rev, m, fstate[fn])
1623 count += pos + neg
1550 count += pos + neg
1624 if pos and not opts['all']:
1551 if pos and not opts['all']:
1625 skip[fn] = True
1552 skip[fn] = True
1553 if copy:
1554 skip[copy] = True
1626 fstate[fn] = m
1555 fstate[fn] = m
1556 if copy:
1557 fstate[copy] = m
1627 prev[fn] = rev
1558 prev[fn] = rev
1628
1559
1629 if not incrementing:
1560 if not incrementing:
@@ -1632,7 +1563,8 b' def grep(ui, repo, pattern, *pats, **opt'
1632 for fn, state in fstate:
1563 for fn, state in fstate:
1633 if fn in skip:
1564 if fn in skip:
1634 continue
1565 continue
1635 display(fn, rev, {}, state)
1566 if fn not in copies.get(prev[fn], {}):
1567 display(fn, rev, {}, state)
1636 return (count == 0 and 1) or 0
1568 return (count == 0 and 1) or 0
1637
1569
1638 def heads(ui, repo, **opts):
1570 def heads(ui, repo, **opts):
@@ -1670,7 +1602,7 b' def identify(ui, repo):'
1670 return
1602 return
1671
1603
1672 hexfunc = ui.verbose and hex or short
1604 hexfunc = ui.verbose and hex or short
1673 modified, added, removed, deleted, unknown = repo.changes()
1605 modified, added, removed, deleted = repo.status()[:4]
1674 output = ["%s%s" %
1606 output = ["%s%s" %
1675 ('+'.join([hexfunc(parent) for parent in parents]),
1607 ('+'.join([hexfunc(parent) for parent in parents]),
1676 (modified or added or removed or deleted) and "+" or "")]
1608 (modified or added or removed or deleted) and "+" or "")]
@@ -1714,81 +1646,23 b' def import_(ui, repo, patch1, *patches, '
1714 d = opts["base"]
1646 d = opts["base"]
1715 strip = opts["strip"]
1647 strip = opts["strip"]
1716
1648
1717 mailre = re.compile(r'(?:From |[\w-]+:)')
1649 wlock = repo.wlock()
1718
1650 lock = repo.lock()
1719 # attempt to detect the start of a patch
1651
1720 # (this heuristic is borrowed from quilt)
1652 for p in patches:
1721 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1653 pf = os.path.join(d, p)
1722 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1654
1723 '(---|\*\*\*)[ \t])', re.MULTILINE)
1724
1725 for patch in patches:
1726 pf = os.path.join(d, patch)
1727
1728 message = None
1729 user = None
1730 date = None
1731 hgpatch = False
1732
1733 p = email.Parser.Parser()
1734 if pf == '-':
1655 if pf == '-':
1735 msg = p.parse(sys.stdin)
1736 ui.status(_("applying patch from stdin\n"))
1656 ui.status(_("applying patch from stdin\n"))
1657 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1737 else:
1658 else:
1738 msg = p.parse(file(pf))
1659 ui.status(_("applying %s\n") % p)
1739 ui.status(_("applying %s\n") % patch)
1660 tmpname, message, user, date = patch.extract(ui, file(pf))
1740
1661
1741 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1662 if tmpname is None:
1742 tmpfp = os.fdopen(fd, 'w')
1663 raise util.Abort(_('no diffs found'))
1664
1743 try:
1665 try:
1744 message = msg['Subject']
1745 if message:
1746 message = message.replace('\n\t', ' ')
1747 ui.debug('Subject: %s\n' % message)
1748 user = msg['From']
1749 if user:
1750 ui.debug('From: %s\n' % user)
1751 diffs_seen = 0
1752 ok_types = ('text/plain', 'text/x-patch')
1753 for part in msg.walk():
1754 content_type = part.get_content_type()
1755 ui.debug('Content-Type: %s\n' % content_type)
1756 if content_type not in ok_types:
1757 continue
1758 payload = part.get_payload(decode=True)
1759 m = diffre.search(payload)
1760 if m:
1761 ui.debug(_('found patch at byte %d\n') % m.start(0))
1762 diffs_seen += 1
1763 hgpatch = False
1764 fp = cStringIO.StringIO()
1765 if message:
1766 fp.write(message)
1767 fp.write('\n')
1768 for line in payload[:m.start(0)].splitlines():
1769 if line.startswith('# HG changeset patch'):
1770 ui.debug(_('patch generated by hg export\n'))
1771 hgpatch = True
1772 # drop earlier commit message content
1773 fp.seek(0)
1774 fp.truncate()
1775 elif hgpatch:
1776 if line.startswith('# User '):
1777 user = line[7:]
1778 ui.debug('From: %s\n' % user)
1779 elif line.startswith("# Date "):
1780 date = line[7:]
1781 if not line.startswith('# '):
1782 fp.write(line)
1783 fp.write('\n')
1784 message = fp.getvalue()
1785 if tmpfp:
1786 tmpfp.write(payload)
1787 if not payload.endswith('\n'):
1788 tmpfp.write('\n')
1789 elif not diffs_seen and message and content_type == 'text/plain':
1790 message += '\n' + payload
1791
1792 if opts['message']:
1666 if opts['message']:
1793 # pickup the cmdline msg
1667 # pickup the cmdline msg
1794 message = opts['message']
1668 message = opts['message']
@@ -1800,14 +1674,9 b' def import_(ui, repo, patch1, *patches, '
1800 message = None
1674 message = None
1801 ui.debug(_('message:\n%s\n') % message)
1675 ui.debug(_('message:\n%s\n') % message)
1802
1676
1803 tmpfp.close()
1677 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1804 if not diffs_seen:
1678 files = patch.updatedir(ui, repo, files, wlock=wlock)
1805 raise util.Abort(_('no diffs found'))
1679 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1806
1807 files = util.patch(strip, tmpname, ui)
1808 if len(files) > 0:
1809 addremove_lock(ui, repo, files, {})
1810 repo.commit(files, message, user, date)
1811 finally:
1680 finally:
1812 os.unlink(tmpname)
1681 os.unlink(tmpname)
1813
1682
@@ -1824,7 +1693,7 b' def incoming(ui, repo, source="default",'
1824 See pull for valid source format details.
1693 See pull for valid source format details.
1825 """
1694 """
1826 source = ui.expandpath(source)
1695 source = ui.expandpath(source)
1827 ui.setconfig_remoteopts(**opts)
1696 setremoteconfig(ui, opts)
1828
1697
1829 other = hg.repository(ui, source)
1698 other = hg.repository(ui, source)
1830 incoming = repo.findincoming(other, force=opts["force"])
1699 incoming = repo.findincoming(other, force=opts["force"])
@@ -1860,7 +1729,7 b' def incoming(ui, repo, source="default",'
1860 displayer.show(changenode=n)
1729 displayer.show(changenode=n)
1861 if opts['patch']:
1730 if opts['patch']:
1862 prev = (parents and parents[0]) or nullid
1731 prev = (parents and parents[0]) or nullid
1863 dodiff(ui, ui, other, prev, n)
1732 patch.diff(repo, other, prev, n)
1864 ui.write("\n")
1733 ui.write("\n")
1865 finally:
1734 finally:
1866 if hasattr(other, 'close'):
1735 if hasattr(other, 'close'):
@@ -1880,7 +1749,7 b' def init(ui, dest=".", **opts):'
1880 Look at the help text for the pull command for important details
1749 Look at the help text for the pull command for important details
1881 about ssh:// URLs.
1750 about ssh:// URLs.
1882 """
1751 """
1883 ui.setconfig_remoteopts(**opts)
1752 setremoteconfig(ui, opts)
1884 hg.repository(ui, dest, create=1)
1753 hg.repository(ui, dest, create=1)
1885
1754
1886 def locate(ui, repo, *pats, **opts):
1755 def locate(ui, repo, *pats, **opts):
@@ -1908,8 +1777,8 b' def locate(ui, repo, *pats, **opts):'
1908 else:
1777 else:
1909 node = None
1778 node = None
1910
1779
1911 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1780 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1912 head='(?:.*/|)'):
1781 head='(?:.*/|)'):
1913 if not node and repo.dirstate.state(abs) == '?':
1782 if not node and repo.dirstate.state(abs) == '?':
1914 continue
1783 continue
1915 if opts['fullpath']:
1784 if opts['fullpath']:
@@ -1920,7 +1789,18 b' def locate(ui, repo, *pats, **opts):'
1920 def log(ui, repo, *pats, **opts):
1789 def log(ui, repo, *pats, **opts):
1921 """show revision history of entire repository or files
1790 """show revision history of entire repository or files
1922
1791
1923 Print the revision history of the specified files or the entire project.
1792 Print the revision history of the specified files or the entire
1793 project.
1794
1795 File history is shown without following rename or copy history of
1796 files. Use -f/--follow with a file name to follow history across
1797 renames and copies. --follow without a file name will only show
1798 ancestors or descendants of the starting revision. --follow-first
1799 only follows the first parent of merge revisions.
1800
1801 If no revision range is specified, the default is tip:0 unless
1802 --follow is set, in which case the working directory parent is
1803 used as the starting revision.
1924
1804
1925 By default this command outputs: changeset id and hash, tags,
1805 By default this command outputs: changeset id and hash, tags,
1926 non-trivial parents, user, date and time, and a summary for each
1806 non-trivial parents, user, date and time, and a summary for each
@@ -2000,7 +1880,7 b' def log(ui, repo, *pats, **opts):'
2000 displayer.show(rev, brinfo=br)
1880 displayer.show(rev, brinfo=br)
2001 if opts['patch']:
1881 if opts['patch']:
2002 prev = (parents and parents[0]) or nullid
1882 prev = (parents and parents[0]) or nullid
2003 dodiff(du, du, repo, prev, changenode, match=matchfn)
1883 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
2004 du.write("\n\n")
1884 du.write("\n\n")
2005 elif st == 'iter':
1885 elif st == 'iter':
2006 if count == limit: break
1886 if count == limit: break
@@ -2031,22 +1911,44 b' def manifest(ui, repo, rev=None):'
2031 else:
1911 else:
2032 n = repo.manifest.tip()
1912 n = repo.manifest.tip()
2033 m = repo.manifest.read(n)
1913 m = repo.manifest.read(n)
2034 mf = repo.manifest.readflags(n)
2035 files = m.keys()
1914 files = m.keys()
2036 files.sort()
1915 files.sort()
2037
1916
2038 for f in files:
1917 for f in files:
2039 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1918 ui.write("%40s %3s %s\n" % (hex(m[f]),
2040
1919 m.execf(f) and "755" or "644", f))
2041 def merge(ui, repo, node=None, **opts):
1920
1921 def merge(ui, repo, node=None, force=None, branch=None):
2042 """Merge working directory with another revision
1922 """Merge working directory with another revision
2043
1923
2044 Merge the contents of the current working directory and the
1924 Merge the contents of the current working directory and the
2045 requested revision. Files that changed between either parent are
1925 requested revision. Files that changed between either parent are
2046 marked as changed for the next commit and a commit must be
1926 marked as changed for the next commit and a commit must be
2047 performed before any further updates are allowed.
1927 performed before any further updates are allowed.
1928
1929 If no revision is specified, the working directory's parent is a
1930 head revision, and the repository contains exactly one other head,
1931 the other head is merged with by default. Otherwise, an explicit
1932 revision to merge with must be provided.
2048 """
1933 """
2049 return doupdate(ui, repo, node=node, merge=True, **opts)
1934
1935 if node:
1936 node = _lookup(repo, node, branch)
1937 else:
1938 heads = repo.heads()
1939 if len(heads) > 2:
1940 raise util.Abort(_('repo has %d heads - '
1941 'please merge with an explicit rev') %
1942 len(heads))
1943 if len(heads) == 1:
1944 raise util.Abort(_('there is nothing to merge - '
1945 'use "hg update" instead'))
1946 parent = repo.dirstate.parents()[0]
1947 if parent not in heads:
1948 raise util.Abort(_('working dir not at a head rev - '
1949 'use "hg update" or merge with an explicit rev'))
1950 node = parent == heads[0] and heads[-1] or heads[0]
1951 return hg.merge(repo, node, force=force)
2050
1952
2051 def outgoing(ui, repo, dest=None, **opts):
1953 def outgoing(ui, repo, dest=None, **opts):
2052 """show changesets not found in destination
1954 """show changesets not found in destination
@@ -2058,7 +1960,7 b' def outgoing(ui, repo, dest=None, **opts'
2058 See pull for valid destination format details.
1960 See pull for valid destination format details.
2059 """
1961 """
2060 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1962 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2061 ui.setconfig_remoteopts(**opts)
1963 setremoteconfig(ui, opts)
2062 revs = None
1964 revs = None
2063 if opts['rev']:
1965 if opts['rev']:
2064 revs = [repo.lookup(rev) for rev in opts['rev']]
1966 revs = [repo.lookup(rev) for rev in opts['rev']]
@@ -2079,16 +1981,31 b' def outgoing(ui, repo, dest=None, **opts'
2079 displayer.show(changenode=n)
1981 displayer.show(changenode=n)
2080 if opts['patch']:
1982 if opts['patch']:
2081 prev = (parents and parents[0]) or nullid
1983 prev = (parents and parents[0]) or nullid
2082 dodiff(ui, ui, repo, prev, n)
1984 patch.diff(repo, prev, n)
2083 ui.write("\n")
1985 ui.write("\n")
2084
1986
2085 def parents(ui, repo, rev=None, branches=None, **opts):
1987 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2086 """show the parents of the working dir or revision
1988 """show the parents of the working dir or revision
2087
1989
2088 Print the working directory's parent revisions.
1990 Print the working directory's parent revisions.
2089 """
1991 """
1992 # legacy
1993 if file_ and not rev:
1994 try:
1995 rev = repo.lookup(file_)
1996 file_ = None
1997 except hg.RepoError:
1998 pass
1999 else:
2000 ui.warn(_("'hg parent REV' is deprecated, "
2001 "please use 'hg parents -r REV instead\n"))
2002
2090 if rev:
2003 if rev:
2091 p = repo.changelog.parents(repo.lookup(rev))
2004 if file_:
2005 ctx = repo.filectx(file_, changeid=rev)
2006 else:
2007 ctx = repo.changectx(rev)
2008 p = [cp.node() for cp in ctx.parents()]
2092 else:
2009 else:
2093 p = repo.dirstate.parents()
2010 p = repo.dirstate.parents()
2094
2011
@@ -2125,7 +2042,7 b' def postincoming(ui, repo, modheads, opt'
2125 return
2042 return
2126 if optupdate:
2043 if optupdate:
2127 if modheads == 1:
2044 if modheads == 1:
2128 return doupdate(ui, repo)
2045 return hg.update(repo, repo.changelog.tip()) # update
2129 else:
2046 else:
2130 ui.status(_("not updating, since new heads added\n"))
2047 ui.status(_("not updating, since new heads added\n"))
2131 if modheads > 1:
2048 if modheads > 1:
@@ -2165,7 +2082,7 b' def pull(ui, repo, source="default", **o'
2165 with the --ssh command line option.
2082 with the --ssh command line option.
2166 """
2083 """
2167 source = ui.expandpath(source)
2084 source = ui.expandpath(source)
2168 ui.setconfig_remoteopts(**opts)
2085 setremoteconfig(ui, opts)
2169
2086
2170 other = hg.repository(ui, source)
2087 other = hg.repository(ui, source)
2171 ui.status(_('pulling from %s\n') % (source))
2088 ui.status(_('pulling from %s\n') % (source))
@@ -2203,7 +2120,7 b' def push(ui, repo, dest=None, **opts):'
2203 feature is enabled on the remote Mercurial server.
2120 feature is enabled on the remote Mercurial server.
2204 """
2121 """
2205 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2122 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2206 ui.setconfig_remoteopts(**opts)
2123 setremoteconfig(ui, opts)
2207
2124
2208 other = hg.repository(ui, dest)
2125 other = hg.repository(ui, dest)
2209 ui.status('pushing to %s\n' % (dest))
2126 ui.status('pushing to %s\n' % (dest))
@@ -2257,7 +2174,7 b' def recover(ui, repo):'
2257 operation. It should only be necessary when Mercurial suggests it.
2174 operation. It should only be necessary when Mercurial suggests it.
2258 """
2175 """
2259 if repo.recover():
2176 if repo.recover():
2260 return repo.verify()
2177 return hg.verify(repo)
2261 return 1
2178 return 1
2262
2179
2263 def remove(ui, repo, *pats, **opts):
2180 def remove(ui, repo, *pats, **opts):
@@ -2277,12 +2194,12 b' def remove(ui, repo, *pats, **opts):'
2277 names = []
2194 names = []
2278 if not opts['after'] and not pats:
2195 if not opts['after'] and not pats:
2279 raise util.Abort(_('no files specified'))
2196 raise util.Abort(_('no files specified'))
2280 files, matchfn, anypats = matchpats(repo, pats, opts)
2197 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2281 exact = dict.fromkeys(files)
2198 exact = dict.fromkeys(files)
2282 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2199 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2283 modified, added, removed, deleted, unknown = mardu
2200 modified, added, removed, deleted, unknown = mardu
2284 remove, forget = [], []
2201 remove, forget = [], []
2285 for src, abs, rel, exact in walk(repo, pats, opts):
2202 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2286 reason = None
2203 reason = None
2287 if abs not in deleted and opts['after']:
2204 if abs not in deleted and opts['after']:
2288 reason = _('is still present')
2205 reason = _('is still present')
@@ -2389,20 +2306,21 b' def revert(ui, repo, *pats, **opts):'
2389
2306
2390 # walk dirstate.
2307 # walk dirstate.
2391
2308
2392 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2309 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2310 badmatch=mf.has_key):
2393 names[abs] = (rel, exact)
2311 names[abs] = (rel, exact)
2394 if src == 'b':
2312 if src == 'b':
2395 target_only[abs] = True
2313 target_only[abs] = True
2396
2314
2397 # walk target manifest.
2315 # walk target manifest.
2398
2316
2399 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2317 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2400 badmatch=names.has_key):
2318 badmatch=names.has_key):
2401 if abs in names: continue
2319 if abs in names: continue
2402 names[abs] = (rel, exact)
2320 names[abs] = (rel, exact)
2403 target_only[abs] = True
2321 target_only[abs] = True
2404
2322
2405 changes = repo.changes(match=names.has_key, wlock=wlock)
2323 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2406 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2324 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2407
2325
2408 revert = ([], _('reverting %s\n'))
2326 revert = ([], _('reverting %s\n'))
@@ -2474,8 +2392,7 b' def revert(ui, repo, *pats, **opts):'
2474
2392
2475 if not opts.get('dry_run'):
2393 if not opts.get('dry_run'):
2476 repo.dirstate.forget(forget[0])
2394 repo.dirstate.forget(forget[0])
2477 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2395 r = hg.revert(repo, node, update.has_key, wlock)
2478 show_stats=False)
2479 repo.dirstate.update(add[0], 'a')
2396 repo.dirstate.update(add[0], 'a')
2480 repo.dirstate.update(undelete[0], 'n')
2397 repo.dirstate.update(undelete[0], 'n')
2481 repo.dirstate.update(remove[0], 'r')
2398 repo.dirstate.update(remove[0], 'r')
@@ -2593,37 +2510,44 b' def serve(ui, repo, **opts):'
2593 def status(ui, repo, *pats, **opts):
2510 def status(ui, repo, *pats, **opts):
2594 """show changed files in the working directory
2511 """show changed files in the working directory
2595
2512
2596 Show changed files in the repository. If names are
2513 Show status of files in the repository. If names are given, only
2597 given, only files that match are shown.
2514 files that match are shown. Files that are clean or ignored, are
2515 not listed unless -c (clean), -i (ignored) or -A is given.
2598
2516
2599 The codes used to show the status of files are:
2517 The codes used to show the status of files are:
2600 M = modified
2518 M = modified
2601 A = added
2519 A = added
2602 R = removed
2520 R = removed
2521 C = clean
2603 ! = deleted, but still tracked
2522 ! = deleted, but still tracked
2604 ? = not tracked
2523 ? = not tracked
2605 I = ignored (not shown by default)
2524 I = ignored (not shown by default)
2606 = the previous added file was copied from here
2525 = the previous added file was copied from here
2607 """
2526 """
2608
2527
2609 show_ignored = opts['ignored'] and True or False
2528 all = opts['all']
2610 files, matchfn, anypats = matchpats(repo, pats, opts)
2529
2530 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2611 cwd = (pats and repo.getcwd()) or ''
2531 cwd = (pats and repo.getcwd()) or ''
2612 modified, added, removed, deleted, unknown, ignored = [
2532 modified, added, removed, deleted, unknown, ignored, clean = [
2613 [util.pathto(cwd, x) for x in n]
2533 [util.pathto(cwd, x) for x in n]
2614 for n in repo.changes(files=files, match=matchfn,
2534 for n in repo.status(files=files, match=matchfn,
2615 show_ignored=show_ignored)]
2535 list_ignored=all or opts['ignored'],
2616
2536 list_clean=all or opts['clean'])]
2617 changetypes = [('modified', 'M', modified),
2537
2538 changetypes = (('modified', 'M', modified),
2618 ('added', 'A', added),
2539 ('added', 'A', added),
2619 ('removed', 'R', removed),
2540 ('removed', 'R', removed),
2620 ('deleted', '!', deleted),
2541 ('deleted', '!', deleted),
2621 ('unknown', '?', unknown),
2542 ('unknown', '?', unknown),
2622 ('ignored', 'I', ignored)]
2543 ('ignored', 'I', ignored))
2544
2545 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2623
2546
2624 end = opts['print0'] and '\0' or '\n'
2547 end = opts['print0'] and '\0' or '\n'
2625
2548
2626 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2549 for opt, char, changes in ([ct for ct in explicit_changetypes
2550 if all or opts[ct[0]]]
2627 or changetypes):
2551 or changetypes):
2628 if opts['no_status']:
2552 if opts['no_status']:
2629 format = "%%s%s" % end
2553 format = "%%s%s" % end
@@ -2632,7 +2556,7 b' def status(ui, repo, *pats, **opts):'
2632
2556
2633 for f in changes:
2557 for f in changes:
2634 ui.write(format % f)
2558 ui.write(format % f)
2635 if (opts.get('copies') and not opts.get('no_status')
2559 if ((all or opts.get('copies')) and not opts.get('no_status')
2636 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2560 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2637 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2561 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2638
2562
@@ -2645,7 +2569,7 b' def tag(ui, repo, name, rev_=None, **opt'
2645 very useful to compare different revision, to go back to significant
2569 very useful to compare different revision, to go back to significant
2646 earlier versions or to mark branch points as releases, etc.
2570 earlier versions or to mark branch points as releases, etc.
2647
2571
2648 If no revision is given, the tip is used.
2572 If no revision is given, the parent of the working directory is used.
2649
2573
2650 To facilitate version control, distribution, and merging of tags,
2574 To facilitate version control, distribution, and merging of tags,
2651 they are stored as a file named ".hgtags" which is managed
2575 they are stored as a file named ".hgtags" which is managed
@@ -2653,8 +2577,8 b' def tag(ui, repo, name, rev_=None, **opt'
2653 necessary. The file '.hg/localtags' is used for local tags (not
2577 necessary. The file '.hg/localtags' is used for local tags (not
2654 shared among repositories).
2578 shared among repositories).
2655 """
2579 """
2656 if name == "tip":
2580 if name in ['tip', '.']:
2657 raise util.Abort(_("the name 'tip' is reserved"))
2581 raise util.Abort(_("the name '%s' is reserved") % name)
2658 if rev_ is not None:
2582 if rev_ is not None:
2659 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2583 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2660 "please use 'hg tag [-r REV] NAME' instead\n"))
2584 "please use 'hg tag [-r REV] NAME' instead\n"))
@@ -2665,7 +2589,12 b' def tag(ui, repo, name, rev_=None, **opt'
2665 if rev_:
2589 if rev_:
2666 r = hex(repo.lookup(rev_))
2590 r = hex(repo.lookup(rev_))
2667 else:
2591 else:
2668 r = hex(repo.changelog.tip())
2592 p1, p2 = repo.dirstate.parents()
2593 if p1 == nullid:
2594 raise util.Abort(_('no revision to tag'))
2595 if p2 != nullid:
2596 raise util.Abort(_('outstanding uncommitted merges'))
2597 r = hex(p1)
2669
2598
2670 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2599 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2671 opts['date'])
2600 opts['date'])
@@ -2701,7 +2630,7 b' def tip(ui, repo, **opts):'
2701 br = repo.branchlookup([n])
2630 br = repo.branchlookup([n])
2702 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2631 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2703 if opts['patch']:
2632 if opts['patch']:
2704 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2633 patch.diff(repo, repo.changelog.parents(n)[0], n)
2705
2634
2706 def unbundle(ui, repo, fname, **opts):
2635 def unbundle(ui, repo, fname, **opts):
2707 """apply a changegroup file
2636 """apply a changegroup file
@@ -2730,7 +2659,8 b' def unbundle(ui, repo, fname, **opts):'
2730 raise util.Abort(_("%s: unknown bundle compression type")
2659 raise util.Abort(_("%s: unknown bundle compression type")
2731 % fname)
2660 % fname)
2732 gen = generator(util.filechunkiter(f, 4096))
2661 gen = generator(util.filechunkiter(f, 4096))
2733 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2662 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2663 'bundle:' + fname)
2734 return postincoming(ui, repo, modheads, opts['update'])
2664 return postincoming(ui, repo, modheads, opts['update'])
2735
2665
2736 def undo(ui, repo):
2666 def undo(ui, repo):
@@ -2745,7 +2675,7 b' def undo(ui, repo):'
2745 repo.rollback()
2675 repo.rollback()
2746
2676
2747 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2677 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2748 branch=None, **opts):
2678 branch=None):
2749 """update or merge working directory
2679 """update or merge working directory
2750
2680
2751 Update the working directory to the specified revision.
2681 Update the working directory to the specified revision.
@@ -2760,13 +2690,17 b' def update(ui, repo, node=None, merge=Fa'
2760 By default, update will refuse to run if doing so would require
2690 By default, update will refuse to run if doing so would require
2761 merging or discarding local changes.
2691 merging or discarding local changes.
2762 """
2692 """
2693 node = _lookup(repo, node, branch)
2763 if merge:
2694 if merge:
2764 ui.warn(_('(the -m/--merge option is deprecated; '
2695 ui.warn(_('(the -m/--merge option is deprecated; '
2765 'use the merge command instead)\n'))
2696 'use the merge command instead)\n'))
2766 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2697 return hg.merge(repo, node, force=force)
2767
2698 elif clean:
2768 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2699 return hg.clean(repo, node)
2769 branch=None, **opts):
2700 else:
2701 return hg.update(repo, node)
2702
2703 def _lookup(repo, node, branch=None):
2770 if branch:
2704 if branch:
2771 br = repo.branchlookup(branch=branch)
2705 br = repo.branchlookup(branch=branch)
2772 found = []
2706 found = []
@@ -2774,19 +2708,19 b' def doupdate(ui, repo, node=None, merge='
2774 if branch in br[x]:
2708 if branch in br[x]:
2775 found.append(x)
2709 found.append(x)
2776 if len(found) > 1:
2710 if len(found) > 1:
2777 ui.warn(_("Found multiple heads for %s\n") % branch)
2711 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2778 for x in found:
2712 for x in found:
2779 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2713 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2780 return 1
2714 raise util.Abort("")
2781 if len(found) == 1:
2715 if len(found) == 1:
2782 node = found[0]
2716 node = found[0]
2783 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2717 repo.ui.warn(_("Using head %s for branch %s\n")
2718 % (short(node), branch))
2784 else:
2719 else:
2785 ui.warn(_("branch %s not found\n") % (branch))
2720 raise util.Abort(_("branch %s not found\n") % (branch))
2786 return 1
2787 else:
2721 else:
2788 node = node and repo.lookup(node) or repo.changelog.tip()
2722 node = node and repo.lookup(node) or repo.changelog.tip()
2789 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2723 return node
2790
2724
2791 def verify(ui, repo):
2725 def verify(ui, repo):
2792 """verify the integrity of the repository
2726 """verify the integrity of the repository
@@ -2798,7 +2732,7 b' def verify(ui, repo):'
2798 the changelog, manifest, and tracked files, as well as the
2732 the changelog, manifest, and tracked files, as well as the
2799 integrity of their crosslinks and indices.
2733 integrity of their crosslinks and indices.
2800 """
2734 """
2801 return repo.verify()
2735 return hg.verify(repo)
2802
2736
2803 # Command options and aliases are listed here, alphabetically
2737 # Command options and aliases are listed here, alphabetically
2804
2738
@@ -2919,6 +2853,7 b' table = {'
2919 ('a', 'text', None, _('treat all files as text')),
2853 ('a', 'text', None, _('treat all files as text')),
2920 ('p', 'show-function', None,
2854 ('p', 'show-function', None,
2921 _('show which function each change is in')),
2855 _('show which function each change is in')),
2856 ('g', 'git', None, _('use git extended diff format')),
2922 ('w', 'ignore-all-space', None,
2857 ('w', 'ignore-all-space', None,
2923 _('ignore white space when comparing lines')),
2858 _('ignore white space when comparing lines')),
2924 ('b', 'ignore-space-change', None,
2859 ('b', 'ignore-space-change', None,
@@ -2943,6 +2878,8 b' table = {'
2943 (grep,
2878 (grep,
2944 [('0', 'print0', None, _('end fields with NUL')),
2879 [('0', 'print0', None, _('end fields with NUL')),
2945 ('', 'all', None, _('print all revisions that match')),
2880 ('', 'all', None, _('print all revisions that match')),
2881 ('f', 'follow', None,
2882 _('follow changeset history, or file history across copies and renames')),
2946 ('i', 'ignore-case', None, _('ignore case when matching')),
2883 ('i', 'ignore-case', None, _('ignore case when matching')),
2947 ('l', 'files-with-matches', None,
2884 ('l', 'files-with-matches', None,
2948 _('print only filenames and revs that match')),
2885 _('print only filenames and revs that match')),
@@ -2979,7 +2916,7 b' table = {'
2979 ('n', 'newest-first', None, _('show newest record first')),
2916 ('n', 'newest-first', None, _('show newest record first')),
2980 ('', 'bundle', '', _('file to store the bundles into')),
2917 ('', 'bundle', '', _('file to store the bundles into')),
2981 ('p', 'patch', None, _('show patch')),
2918 ('p', 'patch', None, _('show patch')),
2982 ('r', 'rev', [], _('a specific revision you would like to pull')),
2919 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2983 ('', 'template', '', _('display with template')),
2920 ('', 'template', '', _('display with template')),
2984 ('e', 'ssh', '', _('specify ssh command to use')),
2921 ('e', 'ssh', '', _('specify ssh command to use')),
2985 ('', 'remotecmd', '',
2922 ('', 'remotecmd', '',
@@ -3005,6 +2942,10 b' table = {'
3005 "^log|history":
2942 "^log|history":
3006 (log,
2943 (log,
3007 [('b', 'branches', None, _('show branches')),
2944 [('b', 'branches', None, _('show branches')),
2945 ('f', 'follow', None,
2946 _('follow changeset history, or file history across copies and renames')),
2947 ('', 'follow-first', None,
2948 _('only follow the first parent of merge changesets')),
3008 ('k', 'keyword', [], _('search for a keyword')),
2949 ('k', 'keyword', [], _('search for a keyword')),
3009 ('l', 'limit', '', _('limit number of changes displayed')),
2950 ('l', 'limit', '', _('limit number of changes displayed')),
3010 ('r', 'rev', [], _('show the specified revision or range')),
2951 ('r', 'rev', [], _('show the specified revision or range')),
@@ -3012,6 +2953,7 b' table = {'
3012 ('', 'style', '', _('display using template map file')),
2953 ('', 'style', '', _('display using template map file')),
3013 ('m', 'only-merges', None, _('show only merges')),
2954 ('m', 'only-merges', None, _('show only merges')),
3014 ('p', 'patch', None, _('show patch')),
2955 ('p', 'patch', None, _('show patch')),
2956 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3015 ('', 'template', '', _('display with template')),
2957 ('', 'template', '', _('display with template')),
3016 ('I', 'include', [], _('include names matching the given patterns')),
2958 ('I', 'include', [], _('include names matching the given patterns')),
3017 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
@@ -3038,9 +2980,10 b' table = {'
3038 "^parents":
2980 "^parents":
3039 (parents,
2981 (parents,
3040 [('b', 'branches', None, _('show branches')),
2982 [('b', 'branches', None, _('show branches')),
2983 ('r', 'rev', '', _('show parents from the specified rev')),
3041 ('', 'style', '', _('display using template map file')),
2984 ('', 'style', '', _('display using template map file')),
3042 ('', 'template', '', _('display with template'))],
2985 ('', 'template', '', _('display with template'))],
3043 _('hg parents [-b] [REV]')),
2986 _('hg parents [-b] [-r REV] [FILE]')),
3044 "paths": (paths, [], _('hg paths [NAME]')),
2987 "paths": (paths, [], _('hg paths [NAME]')),
3045 "^pull":
2988 "^pull":
3046 (pull,
2989 (pull,
@@ -3049,7 +2992,7 b' table = {'
3049 ('e', 'ssh', '', _('specify ssh command to use')),
2992 ('e', 'ssh', '', _('specify ssh command to use')),
3050 ('f', 'force', None,
2993 ('f', 'force', None,
3051 _('run even when remote repository is unrelated')),
2994 _('run even when remote repository is unrelated')),
3052 ('r', 'rev', [], _('a specific revision you would like to pull')),
2995 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3053 ('', 'remotecmd', '',
2996 ('', 'remotecmd', '',
3054 _('specify hg command to run on the remote side'))],
2997 _('specify hg command to run on the remote side'))],
3055 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2998 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
@@ -3117,10 +3060,12 b' table = {'
3117 _('hg serve [OPTION]...')),
3060 _('hg serve [OPTION]...')),
3118 "^status|st":
3061 "^status|st":
3119 (status,
3062 (status,
3120 [('m', 'modified', None, _('show only modified files')),
3063 [('A', 'all', None, _('show status of all files')),
3064 ('m', 'modified', None, _('show only modified files')),
3121 ('a', 'added', None, _('show only added files')),
3065 ('a', 'added', None, _('show only added files')),
3122 ('r', 'removed', None, _('show only removed files')),
3066 ('r', 'removed', None, _('show only removed files')),
3123 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3067 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3068 ('c', 'clean', None, _('show only files without changes')),
3124 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3069 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3125 ('i', 'ignored', None, _('show ignored files')),
3070 ('i', 'ignored', None, _('show ignored files')),
3126 ('n', 'no-status', None, _('hide status prefix')),
3071 ('n', 'no-status', None, _('hide status prefix')),
@@ -3286,24 +3231,16 b' def findext(name):'
3286 try:
3231 try:
3287 return sys.modules[external[name]]
3232 return sys.modules[external[name]]
3288 except KeyError:
3233 except KeyError:
3289 dotname = '.' + name
3290 for k, v in external.iteritems():
3234 for k, v in external.iteritems():
3291 if k.endswith('.' + name) or v == name:
3235 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3292 return sys.modules[v]
3236 return sys.modules[v]
3293 raise KeyError(name)
3237 raise KeyError(name)
3294
3238
3295 def dispatch(args):
3239 def load_extensions(ui):
3296 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3240 added = []
3297 num = getattr(signal, name, None)
3241 for ext_name, load_from_name in ui.extensions():
3298 if num: signal.signal(num, catchterm)
3242 if ext_name in external:
3299
3243 continue
3300 try:
3301 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3302 except util.Abort, inst:
3303 sys.stderr.write(_("abort: %s\n") % inst)
3304 return -1
3305
3306 for ext_name, load_from_name in u.extensions():
3307 try:
3244 try:
3308 if load_from_name:
3245 if load_from_name:
3309 # the module will be loaded in sys.modules
3246 # the module will be loaded in sys.modules
@@ -3323,23 +3260,36 b' def dispatch(args):'
3323 except ImportError:
3260 except ImportError:
3324 mod = importh(ext_name)
3261 mod = importh(ext_name)
3325 external[ext_name] = mod.__name__
3262 external[ext_name] = mod.__name__
3263 added.append((mod, ext_name))
3326 except (util.SignalInterrupt, KeyboardInterrupt):
3264 except (util.SignalInterrupt, KeyboardInterrupt):
3327 raise
3265 raise
3328 except Exception, inst:
3266 except Exception, inst:
3329 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3267 ui.warn(_("*** failed to import extension %s: %s\n") %
3330 if u.print_exc():
3268 (ext_name, inst))
3269 if ui.print_exc():
3331 return 1
3270 return 1
3332
3271
3333 for name in external.itervalues():
3272 for mod, name in added:
3334 mod = sys.modules[name]
3335 uisetup = getattr(mod, 'uisetup', None)
3273 uisetup = getattr(mod, 'uisetup', None)
3336 if uisetup:
3274 if uisetup:
3337 uisetup(u)
3275 uisetup(ui)
3338 cmdtable = getattr(mod, 'cmdtable', {})
3276 cmdtable = getattr(mod, 'cmdtable', {})
3339 for t in cmdtable:
3277 for t in cmdtable:
3340 if t in table:
3278 if t in table:
3341 u.warn(_("module %s overrides %s\n") % (name, t))
3279 ui.warn(_("module %s overrides %s\n") % (name, t))
3342 table.update(cmdtable)
3280 table.update(cmdtable)
3281
3282 def dispatch(args):
3283 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3284 num = getattr(signal, name, None)
3285 if num: signal.signal(num, catchterm)
3286
3287 try:
3288 u = ui.ui(traceback='--traceback' in sys.argv[1:],
3289 readhooks=[load_extensions])
3290 except util.Abort, inst:
3291 sys.stderr.write(_("abort: %s\n") % inst)
3292 return -1
3343
3293
3344 try:
3294 try:
3345 cmd, func, args, options, cmdoptions = parse(u, args)
3295 cmd, func, args, options, cmdoptions = parse(u, args)
@@ -3391,6 +3341,7 b' def dispatch(args):'
3391 mod = sys.modules[name]
3341 mod = sys.modules[name]
3392 if hasattr(mod, 'reposetup'):
3342 if hasattr(mod, 'reposetup'):
3393 mod.reposetup(u, repo)
3343 mod.reposetup(u, repo)
3344 hg.repo_setup_hooks.append(mod.reposetup)
3394 except hg.RepoError:
3345 except hg.RepoError:
3395 if cmd not in optionalrepo.split():
3346 if cmd not in optionalrepo.split():
3396 raise
3347 raise
@@ -3398,6 +3349,11 b' def dispatch(args):'
3398 else:
3349 else:
3399 d = lambda: func(u, *args, **cmdoptions)
3350 d = lambda: func(u, *args, **cmdoptions)
3400
3351
3352 # reupdate the options, repo/.hg/hgrc may have changed them
3353 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3354 not options["noninteractive"], options["traceback"],
3355 options["config"])
3356
3401 try:
3357 try:
3402 if options['profile']:
3358 if options['profile']:
3403 import hotshot, hotshot.stats
3359 import hotshot, hotshot.stats
@@ -1,6 +1,6 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 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.
@@ -11,9 +11,8 b' class changectx(object):'
11 def __init__(self, repo, changeid):
11 def __init__(self, repo, changeid):
12 """changeid is a revision number, node, or tag"""
12 """changeid is a revision number, node, or tag"""
13 self._repo = repo
13 self._repo = repo
14 self._id = changeid
15
14
16 self._node = self._repo.lookup(self._id)
15 self._node = self._repo.lookup(changeid)
17 self._rev = self._repo.changelog.rev(self._node)
16 self._rev = self._repo.changelog.rev(self._node)
18
17
19 def changeset(self):
18 def changeset(self):
@@ -74,39 +73,40 b' class filectx(object):'
74 fileid can be a file revision or node."""
73 fileid can be a file revision or node."""
75 self._repo = repo
74 self._repo = repo
76 self._path = path
75 self._path = path
77 self._id = changeid
78 self._fileid = fileid
79
76
80 if self._id:
77 assert changeid or fileid
78
79 if not fileid:
81 # if given a changeset id, go ahead and look up the file
80 # if given a changeset id, go ahead and look up the file
82 self._changeset = self._repo.changelog.read(self._id)
81 self._changeid = changeid
83 node, flag = self._repo.manifest.find(self._changeset[0], path)
82 self._changectx = self.changectx()
84 self._filelog = self._repo.file(self._path)
83 self._filelog = self._repo.file(self._path)
85 self._filenode = node
84 self._filenode = self._changectx.filenode(self._path)
86 elif self._fileid:
85 else:
87 # else be lazy
86 # else be lazy
88 self._filelog = self._repo.file(self._path)
87 self._filelog = self._repo.file(self._path)
89 self._filenode = self._filelog.lookup(self._fileid)
88 self._filenode = self._filelog.lookup(fileid)
89 self._changeid = self._filelog.linkrev(self._filenode)
90 self._filerev = self._filelog.rev(self._filenode)
90 self._filerev = self._filelog.rev(self._filenode)
91
91
92 def changeset(self):
92 def changectx(self):
93 try:
93 try:
94 return self._changeset
94 return self._changectx
95 except AttributeError:
95 except AttributeError:
96 self._changeset = self._repo.changelog.read(self.node())
96 self._changectx = changectx(self._repo, self._changeid)
97 return self._changeset
97 return self._changectx
98
98
99 def filerev(self): return self._filerev
99 def filerev(self): return self._filerev
100 def filenode(self): return self._filenode
100 def filenode(self): return self._filenode
101 def filelog(self): return self._filelog
101 def filelog(self): return self._filelog
102
102
103 def rev(self): return self.changeset().rev()
103 def rev(self): return self.changectx().rev()
104 def node(self): return self.changeset().node()
104 def node(self): return self.changectx().node()
105 def user(self): return self.changeset().user()
105 def user(self): return self.changectx().user()
106 def date(self): return self.changeset().date()
106 def date(self): return self.changectx().date()
107 def files(self): return self.changeset().files()
107 def files(self): return self.changectx().files()
108 def description(self): return self.changeset().description()
108 def description(self): return self.changectx().description()
109 def manifest(self): return self.changeset().manifest()
109 def manifest(self): return self.changectx().manifest()
110
110
111 def data(self): return self._filelog.read(self._filenode)
111 def data(self): return self._filelog.read(self._filenode)
112 def metadata(self): return self._filelog.readmeta(self._filenode)
112 def metadata(self): return self._filelog.readmeta(self._filenode)
@@ -96,6 +96,7 b' def demandload(scope, modules):'
96
96
97 foo import foo
97 foo import foo
98 foo bar import foo, bar
98 foo bar import foo, bar
99 foo@bar import foo as bar
99 foo.bar import foo.bar
100 foo.bar import foo.bar
100 foo:bar from foo import bar
101 foo:bar from foo import bar
101 foo:bar,quux from foo import bar, quux
102 foo:bar,quux from foo import bar, quux
@@ -108,6 +109,9 b' def demandload(scope, modules):'
108 mod = mod[:col]
109 mod = mod[:col]
109 else:
110 else:
110 fromlist = []
111 fromlist = []
112 as = None
113 if '@' in mod:
114 mod, as = mod.split("@")
111 importer = _importer(scope, mod, fromlist)
115 importer = _importer(scope, mod, fromlist)
112 if fromlist:
116 if fromlist:
113 for name in fromlist:
117 for name in fromlist:
@@ -126,4 +130,6 b' def demandload(scope, modules):'
126 continue
130 continue
127 else:
131 else:
128 basemod = mod
132 basemod = mod
129 scope[basemod] = _replacer(importer, basemod)
133 if not as:
134 as = basemod
135 scope[as] = _replacer(importer, as)
@@ -1,7 +1,7 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
@@ -10,7 +10,7 b' of the GNU General Public License, incor'
10 from node import *
10 from node import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "struct os time bisect stat util re errno")
13 demandload(globals(), "struct os time bisect stat strutil util re errno")
14
14
15 class dirstate(object):
15 class dirstate(object):
16 format = ">cllll"
16 format = ">cllll"
@@ -22,6 +22,7 b' class dirstate(object):'
22 self.ui = ui
22 self.ui = ui
23 self.map = None
23 self.map = None
24 self.pl = None
24 self.pl = None
25 self.dirs = None
25 self.copies = {}
26 self.copies = {}
26 self.ignorefunc = None
27 self.ignorefunc = None
27 self.blockignore = False
28 self.blockignore = False
@@ -197,6 +198,38 b' class dirstate(object):'
197 def copied(self, file):
198 def copied(self, file):
198 return self.copies.get(file, None)
199 return self.copies.get(file, None)
199
200
201 def initdirs(self):
202 if self.dirs is None:
203 self.dirs = {}
204 for f in self.map:
205 self.updatedirs(f, 1)
206
207 def updatedirs(self, path, delta):
208 if self.dirs is not None:
209 for c in strutil.findall(path, '/'):
210 pc = path[:c]
211 self.dirs.setdefault(pc, 0)
212 self.dirs[pc] += delta
213
214 def checkshadows(self, files):
215 def prefixes(f):
216 for c in strutil.rfindall(f, '/'):
217 yield f[:c]
218 self.lazyread()
219 self.initdirs()
220 seendirs = {}
221 for f in files:
222 if self.dirs.get(f):
223 raise util.Abort(_('directory named %r already in dirstate') %
224 f)
225 for d in prefixes(f):
226 if d in seendirs:
227 break
228 if d in self.map:
229 raise util.Abort(_('file named %r already in dirstate') %
230 d)
231 seendirs[d] = True
232
200 def update(self, files, state, **kw):
233 def update(self, files, state, **kw):
201 ''' current states:
234 ''' current states:
202 n normal
235 n normal
@@ -207,10 +240,16 b' class dirstate(object):'
207 if not files: return
240 if not files: return
208 self.lazyread()
241 self.lazyread()
209 self.markdirty()
242 self.markdirty()
243 if state == "a":
244 self.initdirs()
245 self.checkshadows(files)
210 for f in files:
246 for f in files:
211 if state == "r":
247 if state == "r":
212 self.map[f] = ('r', 0, 0, 0)
248 self.map[f] = ('r', 0, 0, 0)
249 self.updatedirs(f, -1)
213 else:
250 else:
251 if state == "a":
252 self.updatedirs(f, 1)
214 s = os.lstat(self.wjoin(f))
253 s = os.lstat(self.wjoin(f))
215 st_size = kw.get('st_size', s.st_size)
254 st_size = kw.get('st_size', s.st_size)
216 st_mtime = kw.get('st_mtime', s.st_mtime)
255 st_mtime = kw.get('st_mtime', s.st_mtime)
@@ -222,9 +261,11 b' class dirstate(object):'
222 if not files: return
261 if not files: return
223 self.lazyread()
262 self.lazyread()
224 self.markdirty()
263 self.markdirty()
264 self.initdirs()
225 for f in files:
265 for f in files:
226 try:
266 try:
227 del self.map[f]
267 del self.map[f]
268 self.updatedirs(f, -1)
228 except KeyError:
269 except KeyError:
229 self.ui.warn(_("not in dirstate: %s!\n") % f)
270 self.ui.warn(_("not in dirstate: %s!\n") % f)
230 pass
271 pass
@@ -232,14 +273,15 b' class dirstate(object):'
232 def clear(self):
273 def clear(self):
233 self.map = {}
274 self.map = {}
234 self.copies = {}
275 self.copies = {}
276 self.dirs = None
235 self.markdirty()
277 self.markdirty()
236
278
237 def rebuild(self, parent, files):
279 def rebuild(self, parent, files):
238 self.clear()
280 self.clear()
239 umask = os.umask(0)
281 umask = os.umask(0)
240 os.umask(umask)
282 os.umask(umask)
241 for f, mode in files:
283 for f in files:
242 if mode:
284 if files.execf(f):
243 self.map[f] = ('n', ~umask, -1, 0)
285 self.map[f] = ('n', ~umask, -1, 0)
244 else:
286 else:
245 self.map[f] = ('n', ~umask & 0666, -1, 0)
287 self.map[f] = ('n', ~umask & 0666, -1, 0)
@@ -344,6 +386,10 b' class dirstate(object):'
344 # directly by this function, but might be modified by your statmatch call.
386 # directly by this function, but might be modified by your statmatch call.
345 #
387 #
346 def walkhelper(self, files, statmatch, dc, badmatch=None):
388 def walkhelper(self, files, statmatch, dc, badmatch=None):
389 # self.root may end with a path separator when self.root == '/'
390 common_prefix_len = len(self.root)
391 if not self.root.endswith('/'):
392 common_prefix_len += 1
347 # recursion free walker, faster than os.walk.
393 # recursion free walker, faster than os.walk.
348 def findfiles(s):
394 def findfiles(s):
349 work = [s]
395 work = [s]
@@ -352,7 +398,7 b' class dirstate(object):'
352 names = os.listdir(top)
398 names = os.listdir(top)
353 names.sort()
399 names.sort()
354 # nd is the top of the repository dir tree
400 # nd is the top of the repository dir tree
355 nd = util.normpath(top[len(self.root) + 1:])
401 nd = util.normpath(top[common_prefix_len:])
356 if nd == '.':
402 if nd == '.':
357 nd = ''
403 nd = ''
358 else:
404 else:
@@ -434,15 +480,16 b' class dirstate(object):'
434 if not seen(k) and (statmatch(k, None)):
480 if not seen(k) and (statmatch(k, None)):
435 yield 'm', k, None
481 yield 'm', k, None
436
482
437 def changes(self, files=None, match=util.always, show_ignored=None):
483 def status(self, files=None, match=util.always, list_ignored=False,
484 list_clean=False):
438 lookup, modified, added, unknown, ignored = [], [], [], [], []
485 lookup, modified, added, unknown, ignored = [], [], [], [], []
439 removed, deleted = [], []
486 removed, deleted, clean = [], [], []
440
487
441 for src, fn, st in self.statwalk(files, match, ignored=show_ignored):
488 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
442 try:
489 try:
443 type_, mode, size, time = self[fn]
490 type_, mode, size, time = self[fn]
444 except KeyError:
491 except KeyError:
445 if show_ignored and self.ignore(fn):
492 if list_ignored and self.ignore(fn):
446 ignored.append(fn)
493 ignored.append(fn)
447 else:
494 else:
448 unknown.append(fn)
495 unknown.append(fn)
@@ -473,6 +520,8 b' class dirstate(object):'
473 modified.append(fn)
520 modified.append(fn)
474 elif time != st.st_mtime:
521 elif time != st.st_mtime:
475 lookup.append(fn)
522 lookup.append(fn)
523 elif list_clean:
524 clean.append(fn)
476 elif type_ == 'm':
525 elif type_ == 'm':
477 modified.append(fn)
526 modified.append(fn)
478 elif type_ == 'a':
527 elif type_ == 'a':
@@ -480,4 +529,5 b' class dirstate(object):'
480 elif type_ == 'r':
529 elif type_ == 'r':
481 removed.append(fn)
530 removed.append(fn)
482
531
483 return (lookup, modified, added, removed, deleted, unknown, ignored)
532 return (lookup, modified, added, removed, deleted, unknown, ignored,
533 clean)
@@ -1,6 +1,6 b''
1 # filelog.py - file history class for mercurial
1 # filelog.py - file history class for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -65,6 +65,26 b' class filelog(revlog):'
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):
69 """return the size of a given revision"""
70
71 # for revisions with renames, we have to go the slow way
72 node = self.node(rev)
73 if self.renamed(node):
74 return len(self.read(node))
75
76 return revlog.size(self, rev)
77
78 def cmp(self, node, text):
79 """compare text with a given file revision"""
80
81 # for renames, we have to go the slow way
82 if self.renamed(node):
83 t2 = self.read(node)
84 return t2 != text
85
86 return revlog.cmp(self, node, text)
87
68 def annotate(self, node):
88 def annotate(self, node):
69
89
70 def decorate(text, rev):
90 def decorate(text, rev):
@@ -76,31 +96,59 b' class filelog(revlog):'
76 return child
96 return child
77
97
78 # find all ancestors
98 # find all ancestors
79 needed = {node:1}
99 needed = {(self, node):1}
80 visit = [node]
100 files = [self]
101 visit = [(self, node)]
81 while visit:
102 while visit:
82 n = visit.pop(0)
103 f, n = visit.pop(0)
83 for p in self.parents(n):
104 rn = f.renamed(n)
84 if p not in needed:
105 if rn:
85 needed[p] = 1
106 f, n = rn
86 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))
87 else:
119 else:
88 # count how many times we'll use this
120 # count how many times we'll use this
89 needed[p] += 1
121 needed[(f, p)] += 1
90
122
91 # sort by revision which is a topological order
123 # sort by revision (per file) which is a topological order
92 visit = [ (self.rev(n), n) for n in needed.keys() ]
124 visit = []
93 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)
94 hist = {}
129 hist = {}
95
130
96 for r,n in visit:
131 for i in range(len(visit)):
97 curr = decorate(self.read(n), self.linkrev(n))
132 r, f, n = visit[i]
98 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:
99 if p != nullid:
147 if p != nullid:
100 curr = pair(hist[p], curr)
148 curr = pair(hist[p], curr)
101 # trim the history of unneeded revs
149 # trim the history of unneeded revs
102 needed[p] -= 1
150 needed[(f, p)] -= 1
103 if not needed[p]:
151 if not needed[(f, p)]:
104 del hist[p]
152 del hist[p]
105 hist[n] = curr
153 hist[n] = curr
106
154
@@ -1,6 +1,7 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
5 #
5 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -10,69 +11,56 b' from repo import *'
10 from demandload import *
11 from demandload import *
11 from i18n import gettext as _
12 from i18n import gettext as _
12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
13 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
13 demandload(globals(), "errno lock os shutil util")
14 demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
14
15 def bundle(ui, path):
16 if path.startswith('bundle://'):
17 path = path[9:]
18 else:
19 path = path[7:]
20 s = path.split("+", 1)
21 if len(s) == 1:
22 repopath, bundlename = "", s[0]
23 else:
24 repopath, bundlename = s
25 return bundlerepo.bundlerepository(ui, repopath, bundlename)
26
27 def hg(ui, path):
28 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
29 return httprepo.httprepository(ui, path.replace("hg://", "http://"))
30
15
31 def local_(ui, path, create=0):
16 def _local(path):
32 if path.startswith('file:'):
17 return (os.path.isfile(path and util.drop_scheme('file', path)) and
33 path = path[5:]
18 bundlerepo or localrepo)
34 return localrepo.localrepository(ui, path, create)
35
36 def ssh_(ui, path, create=0):
37 return sshrepo.sshrepository(ui, path, create)
38
39 def old_http(ui, path):
40 ui.warn(_("old-http:// syntax is deprecated, "
41 "please use static-http:// instead\n"))
42 return statichttprepo.statichttprepository(
43 ui, path.replace("old-http://", "http://"))
44
45 def static_http(ui, path):
46 return statichttprepo.statichttprepository(
47 ui, path.replace("static-http://", "http://"))
48
19
49 schemes = {
20 schemes = {
50 'bundle': bundle,
21 'bundle': bundlerepo,
51 'file': local_,
22 'file': _local,
52 'hg': hg,
23 'hg': httprepo,
53 'http': lambda ui, path: httprepo.httprepository(ui, path),
24 'http': httprepo,
54 'https': lambda ui, path: httprepo.httpsrepository(ui, path),
25 'https': httprepo,
55 'old-http': old_http,
26 'old-http': statichttprepo,
56 'ssh': ssh_,
27 'ssh': sshrepo,
57 'static-http': static_http,
28 'static-http': statichttprepo,
58 }
29 }
59
30
60 def repository(ui, path=None, create=0):
31 def _lookup(path):
61 scheme = None
32 scheme = 'file'
62 if path:
33 if path:
63 c = path.find(':')
34 c = path.find(':')
64 if c > 0:
35 if c > 0:
65 scheme = schemes.get(path[:c])
36 scheme = path[:c]
66 else:
37 thing = schemes.get(scheme) or schemes['file']
67 path = ''
38 try:
68 ctor = scheme or schemes['file']
39 return thing(path)
69 if create:
40 except TypeError:
41 return thing
42
43 def islocal(repo):
44 '''return true if repo or path is local'''
45 if isinstance(repo, str):
70 try:
46 try:
71 return ctor(ui, path, create)
47 return _lookup(repo).islocal(repo)
72 except TypeError:
48 except AttributeError:
73 raise util.Abort(_('cannot create new repository over "%s" protocol') %
49 return False
74 scheme)
50 return repo.local()
75 return ctor(ui, path)
51
52 repo_setup_hooks = []
53
54 def repository(ui, path=None, create=False):
55 """return a repository object for the specified path"""
56 repo = _lookup(path).instance(ui, path, create)
57 for hook in repo_setup_hooks:
58 hook(ui, repo)
59 return repo
60
61 def defaultdest(source):
62 '''return default destination of clone if none is given'''
63 return os.path.basename(os.path.normpath(source))
76
64
77 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
65 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
78 stream=False):
66 stream=False):
@@ -90,7 +78,9 b' def clone(ui, source, dest=None, pull=Fa'
90 If an exception is raised, the partly cloned/updated destination
78 If an exception is raised, the partly cloned/updated destination
91 repository will be deleted.
79 repository will be deleted.
92
80
93 Keyword arguments:
81 Arguments:
82
83 source: repository object or URL
94
84
95 dest: URL of destination repository to create (defaults to base
85 dest: URL of destination repository to create (defaults to base
96 name of source repository)
86 name of source repository)
@@ -105,8 +95,24 b' def clone(ui, source, dest=None, pull=Fa'
105 update: update working directory after clone completes, if
95 update: update working directory after clone completes, if
106 destination is local repository
96 destination is local repository
107 """
97 """
98 if isinstance(source, str):
99 src_repo = repository(ui, source)
100 else:
101 src_repo = source
102 source = src_repo.url()
103
108 if dest is None:
104 if dest is None:
109 dest = os.path.basename(os.path.normpath(source))
105 dest = defaultdest(source)
106
107 def localpath(path):
108 if path.startswith('file://'):
109 return path[7:]
110 if path.startswith('file:'):
111 return path[5:]
112 return path
113
114 dest = localpath(dest)
115 source = localpath(source)
110
116
111 if os.path.exists(dest):
117 if os.path.exists(dest):
112 raise util.Abort(_("destination '%s' already exists"), dest)
118 raise util.Abort(_("destination '%s' already exists"), dest)
@@ -121,8 +127,6 b' def clone(ui, source, dest=None, pull=Fa'
121 if self.dir_:
127 if self.dir_:
122 self.rmtree(self.dir_, True)
128 self.rmtree(self.dir_, True)
123
129
124 src_repo = repository(ui, source)
125
126 dest_repo = None
130 dest_repo = None
127 try:
131 try:
128 dest_repo = repository(ui, dest)
132 dest_repo = repository(ui, dest)
@@ -133,7 +137,7 b' def clone(ui, source, dest=None, pull=Fa'
133 dest_path = None
137 dest_path = None
134 dir_cleanup = None
138 dir_cleanup = None
135 if dest_repo.local():
139 if dest_repo.local():
136 dest_path = os.path.realpath(dest)
140 dest_path = os.path.realpath(dest_repo.root)
137 dir_cleanup = DirCleanup(dest_path)
141 dir_cleanup = DirCleanup(dest_path)
138
142
139 abspath = source
143 abspath = source
@@ -202,8 +206,31 b' def clone(ui, source, dest=None, pull=Fa'
202 dest_lock.release()
206 dest_lock.release()
203
207
204 if update:
208 if update:
205 dest_repo.update(dest_repo.changelog.tip())
209 _merge.update(dest_repo, dest_repo.changelog.tip())
206 if dir_cleanup:
210 if dir_cleanup:
207 dir_cleanup.close()
211 dir_cleanup.close()
208
212
209 return src_repo, dest_repo
213 return src_repo, dest_repo
214
215 def update(repo, node):
216 """update the working directory to node, merging linear changes"""
217 return _merge.update(repo, node)
218
219 def clean(repo, node, wlock=None, show_stats=True):
220 """forcibly switch the working directory to node, clobbering changes"""
221 return _merge.update(repo, node, force=True, wlock=wlock,
222 show_stats=show_stats)
223
224 def merge(repo, node, force=None, remind=True, wlock=None):
225 """branch merge with node, resolving changes"""
226 return _merge.update(repo, node, branchmerge=True, force=force,
227 remind=remind, wlock=wlock)
228
229 def revert(repo, node, choose, wlock):
230 """revert changes to revision in node without updating dirstate"""
231 return _merge.update(repo, node, force=True, partial=choose,
232 show_stats=False, wlock=wlock)
233
234 def verify(repo):
235 """verify the consistency of a repository"""
236 return _verify.verify(repo)
@@ -1,7 +1,7 b''
1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -11,7 +11,7 b' import os.path'
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
15 demandload(globals(), "mercurial:templater")
15 demandload(globals(), "mercurial:templater")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 from mercurial.node import *
17 from mercurial.node import *
@@ -37,6 +37,7 b' class hgweb(object):'
37 self.mtime = -1
37 self.mtime = -1
38 self.reponame = name
38 self.reponame = name
39 self.archives = 'zip', 'gz', 'bz2'
39 self.archives = 'zip', 'gz', 'bz2'
40 self.stripecount = 1
40 self.templatepath = self.repo.ui.config("web", "templates",
41 self.templatepath = self.repo.ui.config("web", "templates",
41 templater.templatepath())
42 templater.templatepath())
42
43
@@ -46,6 +47,8 b' class hgweb(object):'
46 self.mtime = mtime
47 self.mtime = mtime
47 self.repo = hg.repository(self.repo.ui, self.repo.root)
48 self.repo = hg.repository(self.repo.ui, self.repo.root)
48 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
49 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
50 self.stripecount = int(self.repo.ui.config("web", "stripes", 1))
51 self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60))
49 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
52 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
50 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
53 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
51
54
@@ -126,39 +129,29 b' class hgweb(object):'
126 date1 = util.datestr(change1[2])
129 date1 = util.datestr(change1[2])
127 date2 = util.datestr(change2[2])
130 date2 = util.datestr(change2[2])
128
131
129 modified, added, removed, deleted, unknown = r.changes(node1, node2)
132 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
130 if files:
133 if files:
131 modified, added, removed = map(lambda x: filterfiles(files, x),
134 modified, added, removed = map(lambda x: filterfiles(files, x),
132 (modified, added, removed))
135 (modified, added, removed))
133
136
134 diffopts = self.repo.ui.diffopts()
137 diffopts = patch.diffopts(self.repo.ui)
135 showfunc = diffopts['showfunc']
136 ignorews = diffopts['ignorews']
137 ignorewsamount = diffopts['ignorewsamount']
138 ignoreblanklines = diffopts['ignoreblanklines']
139 for f in modified:
138 for f in modified:
140 to = r.file(f).read(mmap1[f])
139 to = r.file(f).read(mmap1[f])
141 tn = r.file(f).read(mmap2[f])
140 tn = r.file(f).read(mmap2[f])
142 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
143 showfunc=showfunc, ignorews=ignorews,
142 opts=diffopts), f, tn)
144 ignorewsamount=ignorewsamount,
145 ignoreblanklines=ignoreblanklines), f, tn)
146 for f in added:
143 for f in added:
147 to = None
144 to = None
148 tn = r.file(f).read(mmap2[f])
145 tn = r.file(f).read(mmap2[f])
149 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
146 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
150 showfunc=showfunc, ignorews=ignorews,
147 opts=diffopts), f, tn)
151 ignorewsamount=ignorewsamount,
152 ignoreblanklines=ignoreblanklines), f, tn)
153 for f in removed:
148 for f in removed:
154 to = r.file(f).read(mmap1[f])
149 to = r.file(f).read(mmap1[f])
155 tn = None
150 tn = None
156 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
151 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
157 showfunc=showfunc, ignorews=ignorews,
152 opts=diffopts), f, tn)
158 ignorewsamount=ignorewsamount,
159 ignoreblanklines=ignoreblanklines), f, tn)
160
153
161 def changelog(self, pos):
154 def changelog(self, pos, shortlog=False):
162 def changenav(**map):
155 def changenav(**map):
163 def seq(factor, maxchanges=None):
156 def seq(factor, maxchanges=None):
164 if maxchanges:
157 if maxchanges:
@@ -173,8 +166,9 b' class hgweb(object):'
173
166
174 l = []
167 l = []
175 last = 0
168 last = 0
176 for f in seq(1, self.maxchanges):
169 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
177 if f < self.maxchanges or f <= last:
170 for f in seq(1, maxchanges):
171 if f < maxchanges or f <= last:
178 continue
172 continue
179 if f > count:
173 if f > count:
180 break
174 break
@@ -219,14 +213,15 b' class hgweb(object):'
219 for e in l:
213 for e in l:
220 yield e
214 yield e
221
215
216 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
222 cl = self.repo.changelog
217 cl = self.repo.changelog
223 mf = cl.read(cl.tip())[0]
218 mf = cl.read(cl.tip())[0]
224 count = cl.count()
219 count = cl.count()
225 start = max(0, pos - self.maxchanges + 1)
220 start = max(0, pos - maxchanges + 1)
226 end = min(count, start + self.maxchanges)
221 end = min(count, start + maxchanges)
227 pos = end - 1
222 pos = end - 1
228
223
229 yield self.t('changelog',
224 yield self.t(shortlog and 'shortlog' or 'changelog',
230 changenav=changenav,
225 changenav=changenav,
231 manifest=hex(mf),
226 manifest=hex(mf),
232 rev=pos, changesets=count, entries=changelist,
227 rev=pos, changesets=count, entries=changelist,
@@ -265,7 +260,7 b' class hgweb(object):'
265 hn = hex(n)
260 hn = hex(n)
266
261
267 yield self.t('searchentry',
262 yield self.t('searchentry',
268 parity=count & 1,
263 parity=self.stripes(count),
269 author=changes[1],
264 author=changes[1],
270 parent=self.siblings(cl.parents(n), cl.rev),
265 parent=self.siblings(cl.parents(n), cl.rev),
271 child=self.siblings(cl.children(n), cl.rev),
266 child=self.siblings(cl.children(n), cl.rev),
@@ -376,7 +371,7 b' class hgweb(object):'
376 for l, t in enumerate(text.splitlines(1)):
371 for l, t in enumerate(text.splitlines(1)):
377 yield {"line": t,
372 yield {"line": t,
378 "linenumber": "% 6d" % (l + 1),
373 "linenumber": "% 6d" % (l + 1),
379 "parity": l & 1}
374 "parity": self.stripes(l)}
380
375
381 yield self.t("filerevision",
376 yield self.t("filerevision",
382 file=f,
377 file=f,
@@ -393,7 +388,7 b' class hgweb(object):'
393 parent=self.siblings(fl.parents(n), fl.rev, file=f),
388 parent=self.siblings(fl.parents(n), fl.rev, file=f),
394 child=self.siblings(fl.children(n), fl.rev, file=f),
389 child=self.siblings(fl.children(n), fl.rev, file=f),
395 rename=self.renamelink(fl, n),
390 rename=self.renamelink(fl, n),
396 permissions=self.repo.manifest.readflags(mfn)[f])
391 permissions=self.repo.manifest.read(mfn).execf(f))
397
392
398 def fileannotate(self, f, node):
393 def fileannotate(self, f, node):
399 bcache = {}
394 bcache = {}
@@ -409,7 +404,7 b' class hgweb(object):'
409 mfn = cs[0]
404 mfn = cs[0]
410
405
411 def annotate(**map):
406 def annotate(**map):
412 parity = 1
407 parity = 0
413 last = None
408 last = None
414 for r, l in fl.annotate(n):
409 for r, l in fl.annotate(n):
415 try:
410 try:
@@ -447,7 +442,7 b' class hgweb(object):'
447 rename=self.renamelink(fl, n),
442 rename=self.renamelink(fl, n),
448 parent=self.siblings(fl.parents(n), fl.rev, file=f),
443 parent=self.siblings(fl.parents(n), fl.rev, file=f),
449 child=self.siblings(fl.children(n), fl.rev, file=f),
444 child=self.siblings(fl.children(n), fl.rev, file=f),
450 permissions=self.repo.manifest.readflags(mfn)[f])
445 permissions=self.repo.manifest.read(mfn).execf(f))
451
446
452 def manifest(self, mnode, path):
447 def manifest(self, mnode, path):
453 man = self.repo.manifest
448 man = self.repo.manifest
@@ -457,7 +452,6 b' class hgweb(object):'
457 rev = man.rev(mn)
452 rev = man.rev(mn)
458 changerev = man.linkrev(mn)
453 changerev = man.linkrev(mn)
459 node = self.repo.changelog.node(changerev)
454 node = self.repo.changelog.node(changerev)
460 mff = man.readflags(mn)
461
455
462 files = {}
456 files = {}
463
457
@@ -489,10 +483,10 b' class hgweb(object):'
489 yield {"file": full,
483 yield {"file": full,
490 "manifest": mnode,
484 "manifest": mnode,
491 "filenode": hex(fnode),
485 "filenode": hex(fnode),
492 "parity": parity,
486 "parity": self.stripes(parity),
493 "basename": f,
487 "basename": f,
494 "permissions": mff[full]}
488 "permissions": mf.execf(full)}
495 parity = 1 - parity
489 parity += 1
496
490
497 def dirlist(**map):
491 def dirlist(**map):
498 parity = 0
492 parity = 0
@@ -503,11 +497,11 b' class hgweb(object):'
503 if fnode:
497 if fnode:
504 continue
498 continue
505
499
506 yield {"parity": parity,
500 yield {"parity": self.stripes(parity),
507 "path": os.path.join(path, f),
501 "path": os.path.join(path, f),
508 "manifest": mnode,
502 "manifest": mnode,
509 "basename": f[:-1]}
503 "basename": f[:-1]}
510 parity = 1 - parity
504 parity += 1
511
505
512 yield self.t("manifest",
506 yield self.t("manifest",
513 manifest=mnode,
507 manifest=mnode,
@@ -530,12 +524,12 b' class hgweb(object):'
530 parity = 0
524 parity = 0
531 for k,n in i:
525 for k,n in i:
532 if notip and k == "tip": continue
526 if notip and k == "tip": continue
533 yield {"parity": parity,
527 yield {"parity": self.stripes(parity),
534 "tag": k,
528 "tag": k,
535 "tagmanifest": hex(cl.read(n)[0]),
529 "tagmanifest": hex(cl.read(n)[0]),
536 "date": cl.read(n)[2],
530 "date": cl.read(n)[2],
537 "node": hex(n)}
531 "node": hex(n)}
538 parity = 1 - parity
532 parity += 1
539
533
540 yield self.t("tags",
534 yield self.t("tags",
541 manifest=hex(mf),
535 manifest=hex(mf),
@@ -565,12 +559,12 b' class hgweb(object):'
565 t = c[2]
559 t = c[2]
566
560
567 yield self.t("tagentry",
561 yield self.t("tagentry",
568 parity = parity,
562 parity = self.stripes(parity),
569 tag = k,
563 tag = k,
570 node = hex(n),
564 node = hex(n),
571 date = t,
565 date = t,
572 tagmanifest = hex(m))
566 tagmanifest = hex(m))
573 parity = 1 - parity
567 parity += 1
574
568
575 def changelist(**map):
569 def changelist(**map):
576 parity = 0
570 parity = 0
@@ -609,7 +603,8 b' class hgweb(object):'
609 lastchange = (0, 0), # FIXME
603 lastchange = (0, 0), # FIXME
610 manifest = hex(mf),
604 manifest = hex(mf),
611 tags = tagentries,
605 tags = tagentries,
612 shortlog = changelist)
606 shortlog = changelist,
607 archives=self.archivelist("tip"))
613
608
614 def filediff(self, file, changeset):
609 def filediff(self, file, changeset):
615 cl = self.repo.changelog
610 cl = self.repo.changelog
@@ -689,6 +684,7 b' class hgweb(object):'
689 def expand_form(form):
684 def expand_form(form):
690 shortcuts = {
685 shortcuts = {
691 'cl': [('cmd', ['changelog']), ('rev', None)],
686 'cl': [('cmd', ['changelog']), ('rev', None)],
687 'sl': [('cmd', ['shortlog']), ('rev', None)],
692 'cs': [('cmd', ['changeset']), ('node', None)],
688 'cs': [('cmd', ['changeset']), ('node', None)],
693 'f': [('cmd', ['file']), ('filenode', None)],
689 'f': [('cmd', ['file']), ('filenode', None)],
694 'fl': [('cmd', ['filelog']), ('filenode', None)],
690 'fl': [('cmd', ['filelog']), ('filenode', None)],
@@ -752,6 +748,13 b' class hgweb(object):'
752 else:
748 else:
753 req.write(self.t("error"))
749 req.write(self.t("error"))
754
750
751 def stripes(self, parity):
752 "make horizontal stripes for easier reading"
753 if self.stripecount:
754 return (1 + parity / self.stripecount) & 1
755 else:
756 return 0
757
755 def do_changelog(self, req):
758 def do_changelog(self, req):
756 hi = self.repo.changelog.count() - 1
759 hi = self.repo.changelog.count() - 1
757 if req.form.has_key('rev'):
760 if req.form.has_key('rev'):
@@ -764,6 +767,18 b' class hgweb(object):'
764
767
765 req.write(self.changelog(hi))
768 req.write(self.changelog(hi))
766
769
770 def do_shortlog(self, req):
771 hi = self.repo.changelog.count() - 1
772 if req.form.has_key('rev'):
773 hi = req.form['rev'][0]
774 try:
775 hi = self.repo.changelog.rev(self.repo.lookup(hi))
776 except hg.RepoError:
777 req.write(self.search(hi)) # XXX redirect to 404 page?
778 return
779
780 req.write(self.changelog(hi, shortlog = True))
781
767 def do_changeset(self, req):
782 def do_changeset(self, req):
768 req.write(self.changeset(req.form['node'][0]))
783 req.write(self.changeset(req.form['node'][0]))
769
784
@@ -895,9 +910,13 b' class hgweb(object):'
895 # require ssl by default, auth info cannot be sniffed and
910 # require ssl by default, auth info cannot be sniffed and
896 # replayed
911 # replayed
897 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
912 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
898 if ssl_req and not req.env.get('HTTPS'):
913 if ssl_req:
899 bail(_('ssl required\n'))
914 if not req.env.get('HTTPS'):
900 return
915 bail(_('ssl required\n'))
916 return
917 proto = 'https'
918 else:
919 proto = 'http'
901
920
902 # do not allow push unless explicitly allowed
921 # do not allow push unless explicitly allowed
903 if not self.check_perm(req, 'push', False):
922 if not self.check_perm(req, 'push', False):
@@ -943,7 +962,9 b' class hgweb(object):'
943 sys.stdout = cStringIO.StringIO()
962 sys.stdout = cStringIO.StringIO()
944
963
945 try:
964 try:
946 ret = self.repo.addchangegroup(fp, 'serve')
965 url = 'remote:%s:%s' % (proto,
966 req.env.get('REMOTE_HOST', ''))
967 ret = self.repo.addchangegroup(fp, 'serve', url)
947 finally:
968 finally:
948 val = sys.stdout.getvalue()
969 val = sys.stdout.getvalue()
949 sys.stdout = old_stdout
970 sys.stdout = old_stdout
@@ -1,7 +1,7 b''
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 # hgweb/request.py - An http request from either CGI or the standalone server.
1 # hgweb/request.py - An http request from either CGI or the standalone server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 # hgweb/server.py - The standalone hg web server.
1 # hgweb/server.py - The standalone hg web server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -198,6 +198,7 b' def create_server(ui, repo):'
198 self.webdirmaker = hgwebdir
198 self.webdirmaker = hgwebdir
199 self.repoviewmaker = hgweb
199 self.repoviewmaker = hgweb
200 self.reqmaker = wsgiapplication(self.make_handler)
200 self.reqmaker = wsgiapplication(self.make_handler)
201 self.daemon_threads = True
201
202
202 def make_handler(self):
203 def make_handler(self):
203 if self.webdir_conf:
204 if self.webdir_conf:
@@ -1,6 +1,6 b''
1 # httprangereader.py - just what it says
1 # httprangereader.py - just what it says
2 #
2 #
3 # Copyright 2005 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.
@@ -1,6 +1,7 b''
1 # httprepo.py - HTTP repository proxy classes for mercurial
1 # httprepo.py - HTTP repository proxy classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
5 #
5 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -115,6 +116,7 b' else:'
115
116
116 class httprepository(remoterepository):
117 class httprepository(remoterepository):
117 def __init__(self, ui, path):
118 def __init__(self, ui, path):
119 self.path = path
118 self.caps = None
120 self.caps = None
119 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
121 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
120 if query or frag:
122 if query or frag:
@@ -124,8 +126,8 b' class httprepository(remoterepository):'
124 host, port, user, passwd = netlocsplit(netloc)
126 host, port, user, passwd = netlocsplit(netloc)
125
127
126 # urllib cannot handle URLs with embedded user or passwd
128 # urllib cannot handle URLs with embedded user or passwd
127 self.url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
129 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
128 urlpath, '', ''))
130 urlpath, '', ''))
129 self.ui = ui
131 self.ui = ui
130
132
131 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
133 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
@@ -189,6 +191,9 b' class httprepository(remoterepository):'
189 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
191 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
190 urllib2.install_opener(opener)
192 urllib2.install_opener(opener)
191
193
194 def url(self):
195 return self.path
196
192 # look up capabilities only when needed
197 # look up capabilities only when needed
193
198
194 def get_caps(self):
199 def get_caps(self):
@@ -213,7 +218,7 b' class httprepository(remoterepository):'
213 q = {"cmd": cmd}
218 q = {"cmd": cmd}
214 q.update(args)
219 q.update(args)
215 qs = urllib.urlencode(q)
220 qs = urllib.urlencode(q)
216 cu = "%s?%s" % (self.url, qs)
221 cu = "%s?%s" % (self._url, qs)
217 try:
222 try:
218 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
223 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
219 except urllib2.HTTPError, inst:
224 except urllib2.HTTPError, inst:
@@ -234,13 +239,13 b' class httprepository(remoterepository):'
234 not proto.startswith('text/plain') and \
239 not proto.startswith('text/plain') and \
235 not proto.startswith('application/hg-changegroup'):
240 not proto.startswith('application/hg-changegroup'):
236 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
241 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
237 self.url)
242 self._url)
238
243
239 if proto.startswith('application/mercurial'):
244 if proto.startswith('application/mercurial'):
240 version = proto[22:]
245 version = proto[22:]
241 if float(version) > 0.1:
246 if float(version) > 0.1:
242 raise hg.RepoError(_("'%s' uses newer protocol %s") %
247 raise hg.RepoError(_("'%s' uses newer protocol %s") %
243 (self.url, version))
248 (self._url, version))
244
249
245 return resp
250 return resp
246
251
@@ -335,3 +340,13 b' class httpsrepository(httprepository):'
335 raise util.Abort(_('Python support for SSL and HTTPS '
340 raise util.Abort(_('Python support for SSL and HTTPS '
336 'is not installed'))
341 'is not installed'))
337 httprepository.__init__(self, ui, path)
342 httprepository.__init__(self, ui, path)
343
344 def instance(ui, path, create):
345 if create:
346 raise util.Abort(_('cannot create new http repository'))
347 if path.startswith('hg:'):
348 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
349 path = 'http:' + path[3:]
350 if path.startswith('https:'):
351 return httpsrepository(ui, path)
352 return httprepository(ui, path)
@@ -1,7 +1,7 b''
1 """
1 """
2 i18n.py - internationalization support for mercurial
2 i18n.py - internationalization support for mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
This diff has been collapsed as it changes many lines, (648 lines changed) Show them Hide them
@@ -1,6 +1,6 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -83,6 +83,9 b' class localrepository(repo.repository):'
83
83
84 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
84 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
85
85
86 def url(self):
87 return 'file:' + self.root
88
86 def hook(self, name, throw=False, **args):
89 def hook(self, name, throw=False, **args):
87 def callhook(hname, funcname):
90 def callhook(hname, funcname):
88 '''call python hook. hook is callable object, looked up as
91 '''call python hook. hook is callable object, looked up as
@@ -195,7 +198,7 b' class localrepository(repo.repository):'
195 self.hook('tag', node=node, tag=name, local=local)
198 self.hook('tag', node=node, tag=name, local=local)
196 return
199 return
197
200
198 for x in self.changes():
201 for x in self.status()[:5]:
199 if '.hgtags' in x:
202 if '.hgtags' in x:
200 raise util.Abort(_('working copy of .hgtags is changed '
203 raise util.Abort(_('working copy of .hgtags is changed '
201 '(please commit .hgtags manually)'))
204 '(please commit .hgtags manually)'))
@@ -289,6 +292,10 b' class localrepository(repo.repository):'
289 try:
292 try:
290 return self.tags()[key]
293 return self.tags()[key]
291 except KeyError:
294 except KeyError:
295 if key == '.':
296 key = self.dirstate.parents()[0]
297 if key == nullid:
298 raise repo.RepoError(_("no revision checked out"))
292 try:
299 try:
293 return self.changelog.lookup(key)
300 return self.changelog.lookup(key)
294 except:
301 except:
@@ -463,8 +470,7 b' class localrepository(repo.repository):'
463 p2 = p2 or self.dirstate.parents()[1] or nullid
470 p2 = p2 or self.dirstate.parents()[1] or nullid
464 c1 = self.changelog.read(p1)
471 c1 = self.changelog.read(p1)
465 c2 = self.changelog.read(p2)
472 c2 = self.changelog.read(p2)
466 m1 = self.manifest.read(c1[0])
473 m1 = self.manifest.read(c1[0]).copy()
467 mf1 = self.manifest.readflags(c1[0])
468 m2 = self.manifest.read(c2[0])
474 m2 = self.manifest.read(c2[0])
469 changed = []
475 changed = []
470
476
@@ -477,36 +483,32 b' class localrepository(repo.repository):'
477 wlock = self.wlock()
483 wlock = self.wlock()
478 l = self.lock()
484 l = self.lock()
479 tr = self.transaction()
485 tr = self.transaction()
480 mm = m1.copy()
481 mfm = mf1.copy()
482 linkrev = self.changelog.count()
486 linkrev = self.changelog.count()
483 for f in files:
487 for f in files:
484 try:
488 try:
485 t = self.wread(f)
489 t = self.wread(f)
486 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
490 m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f)))
487 r = self.file(f)
491 r = self.file(f)
488 mfm[f] = tm
489
492
490 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
493 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
491 if entry:
494 if entry:
492 mm[f] = entry
495 m1[f] = entry
493 continue
496 continue
494
497
495 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
498 m1[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
496 changed.append(f)
499 changed.append(f)
497 if update_dirstate:
500 if update_dirstate:
498 self.dirstate.update([f], "n")
501 self.dirstate.update([f], "n")
499 except IOError:
502 except IOError:
500 try:
503 try:
501 del mm[f]
504 del m1[f]
502 del mfm[f]
503 if update_dirstate:
505 if update_dirstate:
504 self.dirstate.forget([f])
506 self.dirstate.forget([f])
505 except:
507 except:
506 # deleted from p2?
508 # deleted from p2?
507 pass
509 pass
508
510
509 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
511 mnode = self.manifest.add(m1, tr, linkrev, c1[0], c2[0])
510 user = user or self.ui.username()
512 user = user or self.ui.username()
511 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
513 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
512 tr.close()
514 tr.close()
@@ -530,15 +532,14 b' class localrepository(repo.repository):'
530 else:
532 else:
531 self.ui.warn(_("%s not tracked!\n") % f)
533 self.ui.warn(_("%s not tracked!\n") % f)
532 else:
534 else:
533 modified, added, removed, deleted, unknown = self.changes(match=match)
535 modified, added, removed, deleted, unknown = self.status(match=match)[:5]
534 commit = modified + added
536 commit = modified + added
535 remove = removed
537 remove = removed
536
538
537 p1, p2 = self.dirstate.parents()
539 p1, p2 = self.dirstate.parents()
538 c1 = self.changelog.read(p1)
540 c1 = self.changelog.read(p1)
539 c2 = self.changelog.read(p2)
541 c2 = self.changelog.read(p2)
540 m1 = self.manifest.read(c1[0])
542 m1 = self.manifest.read(c1[0]).copy()
541 mf1 = self.manifest.readflags(c1[0])
542 m2 = self.manifest.read(c2[0])
543 m2 = self.manifest.read(c2[0])
543
544
544 if not commit and not remove and not force and p2 == nullid:
545 if not commit and not remove and not force and p2 == nullid:
@@ -564,7 +565,7 b' class localrepository(repo.repository):'
564 for f in commit:
565 for f in commit:
565 self.ui.note(f + "\n")
566 self.ui.note(f + "\n")
566 try:
567 try:
567 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
568 m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f)))
568 t = self.wread(f)
569 t = self.wread(f)
569 except IOError:
570 except IOError:
570 self.ui.warn(_("trouble committing %s!\n") % f)
571 self.ui.warn(_("trouble committing %s!\n") % f)
@@ -591,12 +592,11 b' class localrepository(repo.repository):'
591 changed.append(f)
592 changed.append(f)
592
593
593 # update manifest
594 # update manifest
594 m1 = m1.copy()
595 m1.update(new)
595 m1.update(new)
596 for f in remove:
596 for f in remove:
597 if f in m1:
597 if f in m1:
598 del m1[f]
598 del m1[f]
599 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
599 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0],
600 (new, remove))
600 (new, remove))
601
601
602 # add changeset
602 # add changeset
@@ -658,9 +658,9 b' class localrepository(repo.repository):'
658 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
658 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
659 yield src, fn
659 yield src, fn
660
660
661 def changes(self, node1=None, node2=None, files=[], match=util.always,
661 def status(self, node1=None, node2=None, files=[], match=util.always,
662 wlock=None, show_ignored=None):
662 wlock=None, list_ignored=False, list_clean=False):
663 """return changes between two nodes or node and working directory
663 """return status of files between two nodes or node and working directory
664
664
665 If node1 is None, use the first dirstate parent instead.
665 If node1 is None, use the first dirstate parent instead.
666 If node2 is None, compare node1 with working directory.
666 If node2 is None, compare node1 with working directory.
@@ -668,8 +668,7 b' class localrepository(repo.repository):'
668
668
669 def fcmp(fn, mf):
669 def fcmp(fn, mf):
670 t1 = self.wread(fn)
670 t1 = self.wread(fn)
671 t2 = self.file(fn).read(mf.get(fn, nullid))
671 return self.file(fn).cmp(mf.get(fn, nullid), t1)
672 return cmp(t1, t2)
673
672
674 def mfmatches(node):
673 def mfmatches(node):
675 change = self.changelog.read(node)
674 change = self.changelog.read(node)
@@ -679,7 +678,9 b' class localrepository(repo.repository):'
679 del mf[fn]
678 del mf[fn]
680 return mf
679 return mf
681
680
682 modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[]
681 modified, added, removed, deleted, unknown = [], [], [], [], []
682 ignored, clean = [], []
683
683 compareworking = False
684 compareworking = False
684 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
685 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
685 compareworking = True
686 compareworking = True
@@ -697,8 +698,9 b' class localrepository(repo.repository):'
697 wlock = self.wlock(wait=0)
698 wlock = self.wlock(wait=0)
698 except lock.LockException:
699 except lock.LockException:
699 wlock = None
700 wlock = None
700 lookup, modified, added, removed, deleted, unknown, ignored = (
701 (lookup, modified, added, removed, deleted, unknown,
701 self.dirstate.changes(files, match, show_ignored))
702 ignored, clean) = self.dirstate.status(files, match,
703 list_ignored, list_clean)
702
704
703 # are we comparing working dir against its parent?
705 # are we comparing working dir against its parent?
704 if compareworking:
706 if compareworking:
@@ -721,12 +723,11 b' class localrepository(repo.repository):'
721 del mf2[f]
723 del mf2[f]
722 else:
724 else:
723 # we are comparing two revisions
725 # we are comparing two revisions
724 deleted, unknown, ignored = [], [], []
725 mf2 = mfmatches(node2)
726 mf2 = mfmatches(node2)
726
727
727 if not compareworking:
728 if not compareworking:
728 # flush lists from dirstate before comparing manifests
729 # flush lists from dirstate before comparing manifests
729 modified, added = [], []
730 modified, added, clean = [], [], []
730
731
731 # make sure to sort the files so we talk to the disk in a
732 # make sure to sort the files so we talk to the disk in a
732 # reasonable order
733 # reasonable order
@@ -736,6 +737,8 b' class localrepository(repo.repository):'
736 if mf1.has_key(fn):
737 if mf1.has_key(fn):
737 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
738 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
738 modified.append(fn)
739 modified.append(fn)
740 elif list_clean:
741 clean.append(fn)
739 del mf1[fn]
742 del mf1[fn]
740 else:
743 else:
741 added.append(fn)
744 added.append(fn)
@@ -743,12 +746,9 b' class localrepository(repo.repository):'
743 removed = mf1.keys()
746 removed = mf1.keys()
744
747
745 # sort and return results:
748 # sort and return results:
746 for l in modified, added, removed, deleted, unknown, ignored:
749 for l in modified, added, removed, deleted, unknown, ignored, clean:
747 l.sort()
750 l.sort()
748 if show_ignored is None:
751 return (modified, added, removed, deleted, unknown, ignored, clean)
749 return (modified, added, removed, deleted, unknown)
750 else:
751 return (modified, added, removed, deleted, unknown, ignored)
752
752
753 def add(self, list, wlock=None):
753 def add(self, list, wlock=None):
754 if not wlock:
754 if not wlock:
@@ -798,7 +798,6 b' class localrepository(repo.repository):'
798 def undelete(self, list, wlock=None):
798 def undelete(self, list, wlock=None):
799 p = self.dirstate.parents()[0]
799 p = self.dirstate.parents()[0]
800 mn = self.changelog.read(p)[0]
800 mn = self.changelog.read(p)[0]
801 mf = self.manifest.readflags(mn)
802 m = self.manifest.read(mn)
801 m = self.manifest.read(mn)
803 if not wlock:
802 if not wlock:
804 wlock = self.wlock()
803 wlock = self.wlock()
@@ -808,7 +807,7 b' class localrepository(repo.repository):'
808 else:
807 else:
809 t = self.file(f).read(m[f])
808 t = self.file(f).read(m[f])
810 self.wwrite(f, t)
809 self.wwrite(f, t)
811 util.set_exec(self.wjoin(f), mf[f])
810 util.set_exec(self.wjoin(f), m.execf(f))
812 self.dirstate.update([f], "n")
811 self.dirstate.update([f], "n")
813
812
814 def copy(self, source, dest, wlock=None):
813 def copy(self, source, dest, wlock=None):
@@ -1159,22 +1158,29 b' class localrepository(repo.repository):'
1159 else:
1158 else:
1160 return subset
1159 return subset
1161
1160
1162 def pull(self, remote, heads=None, force=False):
1161 def pull(self, remote, heads=None, force=False, lock=None):
1163 l = self.lock()
1162 mylock = False
1163 if not lock:
1164 lock = self.lock()
1165 mylock = True
1164
1166
1165 fetch = self.findincoming(remote, force=force)
1167 try:
1166 if fetch == [nullid]:
1168 fetch = self.findincoming(remote, force=force)
1167 self.ui.status(_("requesting all changes\n"))
1169 if fetch == [nullid]:
1170 self.ui.status(_("requesting all changes\n"))
1168
1171
1169 if not fetch:
1172 if not fetch:
1170 self.ui.status(_("no changes found\n"))
1173 self.ui.status(_("no changes found\n"))
1171 return 0
1174 return 0
1172
1175
1173 if heads is None:
1176 if heads is None:
1174 cg = remote.changegroup(fetch, 'pull')
1177 cg = remote.changegroup(fetch, 'pull')
1175 else:
1178 else:
1176 cg = remote.changegroupsubset(fetch, heads, 'pull')
1179 cg = remote.changegroupsubset(fetch, heads, 'pull')
1177 return self.addchangegroup(cg, 'pull')
1180 return self.addchangegroup(cg, 'pull', remote.url())
1181 finally:
1182 if mylock:
1183 lock.release()
1178
1184
1179 def push(self, remote, force=False, revs=None):
1185 def push(self, remote, force=False, revs=None):
1180 # there are two ways to push to remote repo:
1186 # there are two ways to push to remote repo:
@@ -1230,7 +1236,7 b' class localrepository(repo.repository):'
1230 ret = self.prepush(remote, force, revs)
1236 ret = self.prepush(remote, force, revs)
1231 if ret[0] is not None:
1237 if ret[0] is not None:
1232 cg, remote_heads = ret
1238 cg, remote_heads = ret
1233 return remote.addchangegroup(cg, 'push')
1239 return remote.addchangegroup(cg, 'push', self.url())
1234 return ret[1]
1240 return ret[1]
1235
1241
1236 def push_unbundle(self, remote, force, revs):
1242 def push_unbundle(self, remote, force, revs):
@@ -1583,7 +1589,7 b' class localrepository(repo.repository):'
1583
1589
1584 return util.chunkbuffer(gengroup())
1590 return util.chunkbuffer(gengroup())
1585
1591
1586 def addchangegroup(self, source, srctype):
1592 def addchangegroup(self, source, srctype, url):
1587 """add changegroup to repo.
1593 """add changegroup to repo.
1588 returns number of heads modified or added + 1."""
1594 returns number of heads modified or added + 1."""
1589
1595
@@ -1597,7 +1603,7 b' class localrepository(repo.repository):'
1597 if not source:
1603 if not source:
1598 return 0
1604 return 0
1599
1605
1600 self.hook('prechangegroup', throw=True, source=srctype)
1606 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1601
1607
1602 changesets = files = revisions = 0
1608 changesets = files = revisions = 0
1603
1609
@@ -1664,544 +1670,21 b' class localrepository(repo.repository):'
1664
1670
1665 if changesets > 0:
1671 if changesets > 0:
1666 self.hook('pretxnchangegroup', throw=True,
1672 self.hook('pretxnchangegroup', throw=True,
1667 node=hex(self.changelog.node(cor+1)), source=srctype)
1673 node=hex(self.changelog.node(cor+1)), source=srctype,
1674 url=url)
1668
1675
1669 tr.close()
1676 tr.close()
1670
1677
1671 if changesets > 0:
1678 if changesets > 0:
1672 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1679 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1673 source=srctype)
1680 source=srctype, url=url)
1674
1681
1675 for i in range(cor + 1, cnr + 1):
1682 for i in range(cor + 1, cnr + 1):
1676 self.hook("incoming", node=hex(self.changelog.node(i)),
1683 self.hook("incoming", node=hex(self.changelog.node(i)),
1677 source=srctype)
1684 source=srctype, url=url)
1678
1685
1679 return newheads - oldheads + 1
1686 return newheads - oldheads + 1
1680
1687
1681 def update(self, node, allow=False, force=False, choose=None,
1682 moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
1683 pl = self.dirstate.parents()
1684 if not force and pl[1] != nullid:
1685 raise util.Abort(_("outstanding uncommitted merges"))
1686
1687 err = False
1688
1689 p1, p2 = pl[0], node
1690 pa = self.changelog.ancestor(p1, p2)
1691 m1n = self.changelog.read(p1)[0]
1692 m2n = self.changelog.read(p2)[0]
1693 man = self.manifest.ancestor(m1n, m2n)
1694 m1 = self.manifest.read(m1n)
1695 mf1 = self.manifest.readflags(m1n)
1696 m2 = self.manifest.read(m2n).copy()
1697 mf2 = self.manifest.readflags(m2n)
1698 ma = self.manifest.read(man)
1699 mfa = self.manifest.readflags(man)
1700
1701 modified, added, removed, deleted, unknown = self.changes()
1702
1703 # is this a jump, or a merge? i.e. is there a linear path
1704 # from p1 to p2?
1705 linear_path = (pa == p1 or pa == p2)
1706
1707 if allow and linear_path:
1708 raise util.Abort(_("there is nothing to merge, just use "
1709 "'hg update' or look at 'hg heads'"))
1710 if allow and not forcemerge:
1711 if modified or added or removed:
1712 raise util.Abort(_("outstanding uncommitted changes"))
1713
1714 if not forcemerge and not force:
1715 for f in unknown:
1716 if f in m2:
1717 t1 = self.wread(f)
1718 t2 = self.file(f).read(m2[f])
1719 if cmp(t1, t2) != 0:
1720 raise util.Abort(_("'%s' already exists in the working"
1721 " dir and differs from remote") % f)
1722
1723 # resolve the manifest to determine which files
1724 # we care about merging
1725 self.ui.note(_("resolving manifests\n"))
1726 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1727 (force, allow, moddirstate, linear_path))
1728 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1729 (short(man), short(m1n), short(m2n)))
1730
1731 merge = {}
1732 get = {}
1733 remove = []
1734
1735 # construct a working dir manifest
1736 mw = m1.copy()
1737 mfw = mf1.copy()
1738 umap = dict.fromkeys(unknown)
1739
1740 for f in added + modified + unknown:
1741 mw[f] = ""
1742 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1743
1744 if moddirstate and not wlock:
1745 wlock = self.wlock()
1746
1747 for f in deleted + removed:
1748 if f in mw:
1749 del mw[f]
1750
1751 # If we're jumping between revisions (as opposed to merging),
1752 # and if neither the working directory nor the target rev has
1753 # the file, then we need to remove it from the dirstate, to
1754 # prevent the dirstate from listing the file when it is no
1755 # longer in the manifest.
1756 if moddirstate and linear_path and f not in m2:
1757 self.dirstate.forget((f,))
1758
1759 # Compare manifests
1760 for f, n in mw.iteritems():
1761 if choose and not choose(f):
1762 continue
1763 if f in m2:
1764 s = 0
1765
1766 # is the wfile new since m1, and match m2?
1767 if f not in m1:
1768 t1 = self.wread(f)
1769 t2 = self.file(f).read(m2[f])
1770 if cmp(t1, t2) == 0:
1771 n = m2[f]
1772 del t1, t2
1773
1774 # are files different?
1775 if n != m2[f]:
1776 a = ma.get(f, nullid)
1777 # are both different from the ancestor?
1778 if n != a and m2[f] != a:
1779 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1780 # merge executable bits
1781 # "if we changed or they changed, change in merge"
1782 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1783 mode = ((a^b) | (a^c)) ^ a
1784 merge[f] = (m1.get(f, nullid), m2[f], mode)
1785 s = 1
1786 # are we clobbering?
1787 # is remote's version newer?
1788 # or are we going back in time?
1789 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1790 self.ui.debug(_(" remote %s is newer, get\n") % f)
1791 get[f] = m2[f]
1792 s = 1
1793 elif f in umap or f in added:
1794 # this unknown file is the same as the checkout
1795 # we need to reset the dirstate if the file was added
1796 get[f] = m2[f]
1797
1798 if not s and mfw[f] != mf2[f]:
1799 if force:
1800 self.ui.debug(_(" updating permissions for %s\n") % f)
1801 util.set_exec(self.wjoin(f), mf2[f])
1802 else:
1803 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1804 mode = ((a^b) | (a^c)) ^ a
1805 if mode != b:
1806 self.ui.debug(_(" updating permissions for %s\n")
1807 % f)
1808 util.set_exec(self.wjoin(f), mode)
1809 del m2[f]
1810 elif f in ma:
1811 if n != ma[f]:
1812 r = _("d")
1813 if not force and (linear_path or allow):
1814 r = self.ui.prompt(
1815 (_(" local changed %s which remote deleted\n") % f) +
1816 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1817 if r == _("d"):
1818 remove.append(f)
1819 else:
1820 self.ui.debug(_("other deleted %s\n") % f)
1821 remove.append(f) # other deleted it
1822 else:
1823 # file is created on branch or in working directory
1824 if force and f not in umap:
1825 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1826 remove.append(f)
1827 elif n == m1.get(f, nullid): # same as parent
1828 if p2 == pa: # going backwards?
1829 self.ui.debug(_("remote deleted %s\n") % f)
1830 remove.append(f)
1831 else:
1832 self.ui.debug(_("local modified %s, keeping\n") % f)
1833 else:
1834 self.ui.debug(_("working dir created %s, keeping\n") % f)
1835
1836 for f, n in m2.iteritems():
1837 if choose and not choose(f):
1838 continue
1839 if f[0] == "/":
1840 continue
1841 if f in ma and n != ma[f]:
1842 r = _("k")
1843 if not force and (linear_path or allow):
1844 r = self.ui.prompt(
1845 (_("remote changed %s which local deleted\n") % f) +
1846 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1847 if r == _("k"):
1848 get[f] = n
1849 elif f not in ma:
1850 self.ui.debug(_("remote created %s\n") % f)
1851 get[f] = n
1852 else:
1853 if force or p2 == pa: # going backwards?
1854 self.ui.debug(_("local deleted %s, recreating\n") % f)
1855 get[f] = n
1856 else:
1857 self.ui.debug(_("local deleted %s\n") % f)
1858
1859 del mw, m1, m2, ma
1860
1861 if force:
1862 for f in merge:
1863 get[f] = merge[f][1]
1864 merge = {}
1865
1866 if linear_path or force:
1867 # we don't need to do any magic, just jump to the new rev
1868 branch_merge = False
1869 p1, p2 = p2, nullid
1870 else:
1871 if not allow:
1872 self.ui.status(_("this update spans a branch"
1873 " affecting the following files:\n"))
1874 fl = merge.keys() + get.keys()
1875 fl.sort()
1876 for f in fl:
1877 cf = ""
1878 if f in merge:
1879 cf = _(" (resolve)")
1880 self.ui.status(" %s%s\n" % (f, cf))
1881 self.ui.warn(_("aborting update spanning branches!\n"))
1882 self.ui.status(_("(use 'hg merge' to merge across branches"
1883 " or 'hg update -C' to lose changes)\n"))
1884 return 1
1885 branch_merge = True
1886
1887 xp1 = hex(p1)
1888 xp2 = hex(p2)
1889 if p2 == nullid: xxp2 = ''
1890 else: xxp2 = xp2
1891
1892 self.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
1893
1894 # get the files we don't need to change
1895 files = get.keys()
1896 files.sort()
1897 for f in files:
1898 if f[0] == "/":
1899 continue
1900 self.ui.note(_("getting %s\n") % f)
1901 t = self.file(f).read(get[f])
1902 self.wwrite(f, t)
1903 util.set_exec(self.wjoin(f), mf2[f])
1904 if moddirstate:
1905 if branch_merge:
1906 self.dirstate.update([f], 'n', st_mtime=-1)
1907 else:
1908 self.dirstate.update([f], 'n')
1909
1910 # merge the tricky bits
1911 failedmerge = []
1912 files = merge.keys()
1913 files.sort()
1914 for f in files:
1915 self.ui.status(_("merging %s\n") % f)
1916 my, other, flag = merge[f]
1917 ret = self.merge3(f, my, other, xp1, xp2)
1918 if ret:
1919 err = True
1920 failedmerge.append(f)
1921 util.set_exec(self.wjoin(f), flag)
1922 if moddirstate:
1923 if branch_merge:
1924 # We've done a branch merge, mark this file as merged
1925 # so that we properly record the merger later
1926 self.dirstate.update([f], 'm')
1927 else:
1928 # We've update-merged a locally modified file, so
1929 # we set the dirstate to emulate a normal checkout
1930 # of that file some time in the past. Thus our
1931 # merge will appear as a normal local file
1932 # modification.
1933 f_len = len(self.file(f).read(other))
1934 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1935
1936 remove.sort()
1937 for f in remove:
1938 self.ui.note(_("removing %s\n") % f)
1939 util.audit_path(f)
1940 try:
1941 util.unlink(self.wjoin(f))
1942 except OSError, inst:
1943 if inst.errno != errno.ENOENT:
1944 self.ui.warn(_("update failed to remove %s: %s!\n") %
1945 (f, inst.strerror))
1946 if moddirstate:
1947 if branch_merge:
1948 self.dirstate.update(remove, 'r')
1949 else:
1950 self.dirstate.forget(remove)
1951
1952 if moddirstate:
1953 self.dirstate.setparents(p1, p2)
1954
1955 if show_stats:
1956 stats = ((len(get), _("updated")),
1957 (len(merge) - len(failedmerge), _("merged")),
1958 (len(remove), _("removed")),
1959 (len(failedmerge), _("unresolved")))
1960 note = ", ".join([_("%d files %s") % s for s in stats])
1961 self.ui.status("%s\n" % note)
1962 if moddirstate:
1963 if branch_merge:
1964 if failedmerge:
1965 self.ui.status(_("There are unresolved merges,"
1966 " you can redo the full merge using:\n"
1967 " hg update -C %s\n"
1968 " hg merge %s\n"
1969 % (self.changelog.rev(p1),
1970 self.changelog.rev(p2))))
1971 else:
1972 self.ui.status(_("(branch merge, don't forget to commit)\n"))
1973 elif failedmerge:
1974 self.ui.status(_("There are unresolved merges with"
1975 " locally modified files.\n"))
1976
1977 self.hook('update', parent1=xp1, parent2=xxp2, error=int(err))
1978 return err
1979
1980 def merge3(self, fn, my, other, p1, p2):
1981 """perform a 3-way merge in the working directory"""
1982
1983 def temp(prefix, node):
1984 pre = "%s~%s." % (os.path.basename(fn), prefix)
1985 (fd, name) = tempfile.mkstemp(prefix=pre)
1986 f = os.fdopen(fd, "wb")
1987 self.wwrite(fn, fl.read(node), f)
1988 f.close()
1989 return name
1990
1991 fl = self.file(fn)
1992 base = fl.ancestor(my, other)
1993 a = self.wjoin(fn)
1994 b = temp("base", base)
1995 c = temp("other", other)
1996
1997 self.ui.note(_("resolving %s\n") % fn)
1998 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1999 (fn, short(my), short(other), short(base)))
2000
2001 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
2002 or "hgmerge")
2003 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
2004 environ={'HG_FILE': fn,
2005 'HG_MY_NODE': p1,
2006 'HG_OTHER_NODE': p2,
2007 'HG_FILE_MY_NODE': hex(my),
2008 'HG_FILE_OTHER_NODE': hex(other),
2009 'HG_FILE_BASE_NODE': hex(base)})
2010 if r:
2011 self.ui.warn(_("merging %s failed!\n") % fn)
2012
2013 os.unlink(b)
2014 os.unlink(c)
2015 return r
2016
2017 def verify(self):
2018 filelinkrevs = {}
2019 filenodes = {}
2020 changesets = revisions = files = 0
2021 errors = [0]
2022 warnings = [0]
2023 neededmanifests = {}
2024
2025 def err(msg):
2026 self.ui.warn(msg + "\n")
2027 errors[0] += 1
2028
2029 def warn(msg):
2030 self.ui.warn(msg + "\n")
2031 warnings[0] += 1
2032
2033 def checksize(obj, name):
2034 d = obj.checksize()
2035 if d[0]:
2036 err(_("%s data length off by %d bytes") % (name, d[0]))
2037 if d[1]:
2038 err(_("%s index contains %d extra bytes") % (name, d[1]))
2039
2040 def checkversion(obj, name):
2041 if obj.version != revlog.REVLOGV0:
2042 if not revlogv1:
2043 warn(_("warning: `%s' uses revlog format 1") % name)
2044 elif revlogv1:
2045 warn(_("warning: `%s' uses revlog format 0") % name)
2046
2047 revlogv1 = self.revlogversion != revlog.REVLOGV0
2048 if self.ui.verbose or revlogv1 != self.revlogv1:
2049 self.ui.status(_("repository uses revlog format %d\n") %
2050 (revlogv1 and 1 or 0))
2051
2052 seen = {}
2053 self.ui.status(_("checking changesets\n"))
2054 checksize(self.changelog, "changelog")
2055
2056 for i in range(self.changelog.count()):
2057 changesets += 1
2058 n = self.changelog.node(i)
2059 l = self.changelog.linkrev(n)
2060 if l != i:
2061 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
2062 if n in seen:
2063 err(_("duplicate changeset at revision %d") % i)
2064 seen[n] = 1
2065
2066 for p in self.changelog.parents(n):
2067 if p not in self.changelog.nodemap:
2068 err(_("changeset %s has unknown parent %s") %
2069 (short(n), short(p)))
2070 try:
2071 changes = self.changelog.read(n)
2072 except KeyboardInterrupt:
2073 self.ui.warn(_("interrupted"))
2074 raise
2075 except Exception, inst:
2076 err(_("unpacking changeset %s: %s") % (short(n), inst))
2077 continue
2078
2079 neededmanifests[changes[0]] = n
2080
2081 for f in changes[3]:
2082 filelinkrevs.setdefault(f, []).append(i)
2083
2084 seen = {}
2085 self.ui.status(_("checking manifests\n"))
2086 checkversion(self.manifest, "manifest")
2087 checksize(self.manifest, "manifest")
2088
2089 for i in range(self.manifest.count()):
2090 n = self.manifest.node(i)
2091 l = self.manifest.linkrev(n)
2092
2093 if l < 0 or l >= self.changelog.count():
2094 err(_("bad manifest link (%d) at revision %d") % (l, i))
2095
2096 if n in neededmanifests:
2097 del neededmanifests[n]
2098
2099 if n in seen:
2100 err(_("duplicate manifest at revision %d") % i)
2101
2102 seen[n] = 1
2103
2104 for p in self.manifest.parents(n):
2105 if p not in self.manifest.nodemap:
2106 err(_("manifest %s has unknown parent %s") %
2107 (short(n), short(p)))
2108
2109 try:
2110 delta = mdiff.patchtext(self.manifest.delta(n))
2111 except KeyboardInterrupt:
2112 self.ui.warn(_("interrupted"))
2113 raise
2114 except Exception, inst:
2115 err(_("unpacking manifest %s: %s") % (short(n), inst))
2116 continue
2117
2118 try:
2119 ff = [ l.split('\0') for l in delta.splitlines() ]
2120 for f, fn in ff:
2121 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
2122 except (ValueError, TypeError), inst:
2123 err(_("broken delta in manifest %s: %s") % (short(n), inst))
2124
2125 self.ui.status(_("crosschecking files in changesets and manifests\n"))
2126
2127 for m, c in neededmanifests.items():
2128 err(_("Changeset %s refers to unknown manifest %s") %
2129 (short(m), short(c)))
2130 del neededmanifests
2131
2132 for f in filenodes:
2133 if f not in filelinkrevs:
2134 err(_("file %s in manifest but not in changesets") % f)
2135
2136 for f in filelinkrevs:
2137 if f not in filenodes:
2138 err(_("file %s in changeset but not in manifest") % f)
2139
2140 self.ui.status(_("checking files\n"))
2141 ff = filenodes.keys()
2142 ff.sort()
2143 for f in ff:
2144 if f == "/dev/null":
2145 continue
2146 files += 1
2147 if not f:
2148 err(_("file without name in manifest %s") % short(n))
2149 continue
2150 fl = self.file(f)
2151 checkversion(fl, f)
2152 checksize(fl, f)
2153
2154 nodes = {nullid: 1}
2155 seen = {}
2156 for i in range(fl.count()):
2157 revisions += 1
2158 n = fl.node(i)
2159
2160 if n in seen:
2161 err(_("%s: duplicate revision %d") % (f, i))
2162 if n not in filenodes[f]:
2163 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
2164 else:
2165 del filenodes[f][n]
2166
2167 flr = fl.linkrev(n)
2168 if flr not in filelinkrevs.get(f, []):
2169 err(_("%s:%s points to unexpected changeset %d")
2170 % (f, short(n), flr))
2171 else:
2172 filelinkrevs[f].remove(flr)
2173
2174 # verify contents
2175 try:
2176 t = fl.read(n)
2177 except KeyboardInterrupt:
2178 self.ui.warn(_("interrupted"))
2179 raise
2180 except Exception, inst:
2181 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
2182
2183 # verify parents
2184 (p1, p2) = fl.parents(n)
2185 if p1 not in nodes:
2186 err(_("file %s:%s unknown parent 1 %s") %
2187 (f, short(n), short(p1)))
2188 if p2 not in nodes:
2189 err(_("file %s:%s unknown parent 2 %s") %
2190 (f, short(n), short(p1)))
2191 nodes[n] = 1
2192
2193 # cross-check
2194 for node in filenodes[f]:
2195 err(_("node %s in manifests not in %s") % (hex(node), f))
2196
2197 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
2198 (files, changesets, revisions))
2199
2200 if warnings[0]:
2201 self.ui.warn(_("%d warnings encountered!\n") % warnings[0])
2202 if errors[0]:
2203 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
2204 return 1
2205
1688
2206 def stream_in(self, remote):
1689 def stream_in(self, remote):
2207 fp = remote.stream_out()
1690 fp = remote.stream_out()
@@ -2227,7 +1710,7 b' class localrepository(repo.repository):'
2227 util.bytecount(total_bytes / elapsed)))
1710 util.bytecount(total_bytes / elapsed)))
2228 self.reload()
1711 self.reload()
2229 return len(self.heads()) + 1
1712 return len(self.heads()) + 1
2230
1713
2231 def clone(self, remote, heads=[], stream=False):
1714 def clone(self, remote, heads=[], stream=False):
2232 '''clone remote repository.
1715 '''clone remote repository.
2233
1716
@@ -2256,3 +1739,8 b' def aftertrans(base):'
2256 os.path.join(p, "undo.dirstate"))
1739 os.path.join(p, "undo.dirstate"))
2257 return a
1740 return a
2258
1741
1742 def instance(ui, path, create):
1743 return localrepository(ui, util.drop_scheme('file', path), create)
1744
1745 def islocal(path):
1746 return True
@@ -1,6 +1,6 b''
1 # lock.py - simple locking scheme for mercurial
1 # lock.py - simple locking scheme for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -1,6 +1,6 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -10,6 +10,31 b' from i18n import gettext as _'
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "array bisect struct")
11 demandload(globals(), "array bisect struct")
12
12
13 class manifestdict(dict):
14 def __init__(self, mapping=None, flags=None):
15 if mapping is None: mapping = {}
16 if flags is None: flags = {}
17 dict.__init__(self, mapping)
18 self._flags = flags
19 def flags(self, f):
20 return self._flags.get(f, "")
21 def execf(self, f):
22 "test for executable in manifest flags"
23 return "x" in self.flags(f)
24 def linkf(self, f):
25 "test for symlink in manifest flags"
26 return "l" in self.flags(f)
27 def rawset(self, f, entry):
28 self[f] = bin(entry[:40])
29 fl = entry[40:-1]
30 if fl: self._flags[f] = fl
31 def set(self, f, execf=False, linkf=False):
32 if linkf: self._flags[f] = "l"
33 elif execf: self._flags[f] = "x"
34 else: self._flags[f] = ""
35 def copy(self):
36 return manifestdict(dict.copy(self), dict.copy(self._flags))
37
13 class manifest(revlog):
38 class manifest(revlog):
14 def __init__(self, opener, defversion=REVLOGV0):
39 def __init__(self, opener, defversion=REVLOGV0):
15 self.mapcache = None
40 self.mapcache = None
@@ -18,26 +43,18 b' class manifest(revlog):'
18 defversion)
43 defversion)
19
44
20 def read(self, node):
45 def read(self, node):
21 if node == nullid: return {} # don't upset local cache
46 if node == nullid: return manifestdict() # don't upset local cache
22 if self.mapcache and self.mapcache[0] == node:
47 if self.mapcache and self.mapcache[0] == node:
23 return self.mapcache[1]
48 return self.mapcache[1]
24 text = self.revision(node)
49 text = self.revision(node)
25 map = {}
26 flag = {}
27 self.listcache = array.array('c', text)
50 self.listcache = array.array('c', text)
28 lines = text.splitlines(1)
51 lines = text.splitlines(1)
52 mapping = manifestdict()
29 for l in lines:
53 for l in lines:
30 (f, n) = l.split('\0')
54 (f, n) = l.split('\0')
31 map[f] = bin(n[:40])
55 mapping.rawset(f, n)
32 flag[f] = (n[40:-1] == "x")
56 self.mapcache = (node, mapping)
33 self.mapcache = (node, map, flag)
57 return mapping
34 return map
35
36 def readflags(self, node):
37 if node == nullid: return {} # don't upset local cache
38 if not self.mapcache or self.mapcache[0] != node:
39 self.read(node)
40 return self.mapcache[2]
41
58
42 def diff(self, a, b):
59 def diff(self, a, b):
43 return mdiff.textdiff(str(a), str(b))
60 return mdiff.textdiff(str(a), str(b))
@@ -86,7 +103,7 b' class manifest(revlog):'
86 '''look up entry for a single file efficiently.
103 '''look up entry for a single file efficiently.
87 return (node, flag) pair if found, (None, None) if not.'''
104 return (node, flag) pair if found, (None, None) if not.'''
88 if self.mapcache and node == self.mapcache[0]:
105 if self.mapcache and node == self.mapcache[0]:
89 return self.mapcache[1].get(f), self.mapcache[2].get(f)
106 return self.mapcache[1].get(f), self.mapcache[1].flags(f)
90 text = self.revision(node)
107 text = self.revision(node)
91 start, end = self._search(text, f)
108 start, end = self._search(text, f)
92 if start == end:
109 if start == end:
@@ -95,7 +112,7 b' class manifest(revlog):'
95 f, n = l.split('\0')
112 f, n = l.split('\0')
96 return bin(n[:40]), n[40:-1] == 'x'
113 return bin(n[:40]), n[40:-1] == 'x'
97
114
98 def add(self, map, flags, transaction, link, p1=None, p2=None,
115 def add(self, map, transaction, link, p1=None, p2=None,
99 changed=None):
116 changed=None):
100 # apply the changes collected during the bisect loop to our addlist
117 # apply the changes collected during the bisect loop to our addlist
101 # return a delta suitable for addrevision
118 # return a delta suitable for addrevision
@@ -123,9 +140,7 b' class manifest(revlog):'
123
140
124 # if this is changed to support newlines in filenames,
141 # if this is changed to support newlines in filenames,
125 # be sure to check the templates/ dir again (especially *-raw.tmpl)
142 # be sure to check the templates/ dir again (especially *-raw.tmpl)
126 text = ["%s\000%s%s\n" %
143 text = ["%s\000%s%s\n" % (f, hex(map[f]), map.flags(f)) for f in files]
127 (f, hex(map[f]), flags[f] and "x" or '')
128 for f in files]
129 self.listcache = array.array('c', "".join(text))
144 self.listcache = array.array('c', "".join(text))
130 cachedelta = None
145 cachedelta = None
131 else:
146 else:
@@ -151,8 +166,7 b' class manifest(revlog):'
151 # bs will either be the index of the item or the insert point
166 # bs will either be the index of the item or the insert point
152 start, end = self._search(addbuf, f, start)
167 start, end = self._search(addbuf, f, start)
153 if w[1] == 0:
168 if w[1] == 0:
154 l = "%s\000%s%s\n" % (f, hex(map[f]),
169 l = "%s\000%s%s\n" % (f, hex(map[f]), map.flags(f))
155 flags[f] and "x" or '')
156 else:
170 else:
157 l = ""
171 l = ""
158 if start == end and w[1] == 1:
172 if start == end and w[1] == 1:
@@ -183,6 +197,6 b' class manifest(revlog):'
183
197
184 n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
198 n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
185 p2, cachedelta)
199 p2, cachedelta)
186 self.mapcache = (n, map, flags)
200 self.mapcache = (n, map)
187
201
188 return n
202 return n
@@ -1,6 +1,6 b''
1 # mdiff.py - diff and patch routines for mercurial
1 # mdiff.py - diff and patch routines for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -19,14 +19,41 b' def splitnewlines(text):'
19 lines[-1] = lines[-1][:-1]
19 lines[-1] = lines[-1][:-1]
20 return lines
20 return lines
21
21
22 def unidiff(a, ad, b, bd, fn, r=None, text=False,
22 class diffopts(object):
23 showfunc=False, ignorews=False, ignorewsamount=False,
23 '''context is the number of context lines
24 ignoreblanklines=False):
24 text treats all files as text
25 showfunc enables diff -p output
26 git enables the git extended patch format
27 ignorews ignores all whitespace changes in the diff
28 ignorewsamount ignores changes in the amount of whitespace
29 ignoreblanklines ignores changes whose lines are all blank'''
25
30
31 defaults = {
32 'context': 3,
33 'text': False,
34 'showfunc': True,
35 'git': False,
36 'ignorews': False,
37 'ignorewsamount': False,
38 'ignoreblanklines': False,
39 }
40
41 __slots__ = defaults.keys()
42
43 def __init__(self, **opts):
44 for k in self.__slots__:
45 v = opts.get(k)
46 if v is None:
47 v = self.defaults[k]
48 setattr(self, k, v)
49
50 defaultopts = diffopts()
51
52 def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts):
26 if not a and not b: return ""
53 if not a and not b: return ""
27 epoch = util.datestr((0, 0))
54 epoch = util.datestr((0, 0))
28
55
29 if not text and (util.binary(a) or util.binary(b)):
56 if not opts.text and (util.binary(a) or util.binary(b)):
30 l = ['Binary file %s has changed\n' % fn]
57 l = ['Binary file %s has changed\n' % fn]
31 elif not a:
58 elif not a:
32 b = splitnewlines(b)
59 b = splitnewlines(b)
@@ -49,10 +76,7 b' def unidiff(a, ad, b, bd, fn, r=None, te'
49 else:
76 else:
50 al = splitnewlines(a)
77 al = splitnewlines(a)
51 bl = splitnewlines(b)
78 bl = splitnewlines(b)
52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
79 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, opts=opts))
53 showfunc=showfunc, ignorews=ignorews,
54 ignorewsamount=ignorewsamount,
55 ignoreblanklines=ignoreblanklines))
56 if not l: return ""
80 if not l: return ""
57 # difflib uses a space, rather than a tab
81 # difflib uses a space, rather than a tab
58 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
82 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
@@ -72,21 +96,15 b' def unidiff(a, ad, b, bd, fn, r=None, te'
72 # t1 and t2 are the text to be diffed
96 # t1 and t2 are the text to be diffed
73 # l1 and l2 are the text broken up into lines
97 # l1 and l2 are the text broken up into lines
74 # header1 and header2 are the filenames for the diff output
98 # header1 and header2 are the filenames for the diff output
75 # context is the number of context lines
99 def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
76 # showfunc enables diff -p output
77 # ignorews ignores all whitespace changes in the diff
78 # ignorewsamount ignores changes in the amount of whitespace
79 # ignoreblanklines ignores changes whose lines are all blank
80 def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
81 ignorews=False, ignorewsamount=False, ignoreblanklines=False):
82 def contextend(l, len):
100 def contextend(l, len):
83 ret = l + context
101 ret = l + opts.context
84 if ret > len:
102 if ret > len:
85 ret = len
103 ret = len
86 return ret
104 return ret
87
105
88 def contextstart(l):
106 def contextstart(l):
89 ret = l - context
107 ret = l - opts.context
90 if ret < 0:
108 if ret < 0:
91 return 0
109 return 0
92 return ret
110 return ret
@@ -101,7 +119,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
101 blen = b2 - bstart + aend - a2
119 blen = b2 - bstart + aend - a2
102
120
103 func = ""
121 func = ""
104 if showfunc:
122 if opts.showfunc:
105 # walk backwards from the start of the context
123 # walk backwards from the start of the context
106 # to find a line starting with an alphanumeric char.
124 # to find a line starting with an alphanumeric char.
107 for x in xrange(astart, -1, -1):
125 for x in xrange(astart, -1, -1):
@@ -119,14 +137,14 b' def bunidiff(t1, t2, l1, l2, header1, he'
119
137
120 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
138 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
121
139
122 if showfunc:
140 if opts.showfunc:
123 funcre = re.compile('\w')
141 funcre = re.compile('\w')
124 if ignorewsamount:
142 if opts.ignorewsamount:
125 wsamountre = re.compile('[ \t]+')
143 wsamountre = re.compile('[ \t]+')
126 wsappendedre = re.compile(' \n')
144 wsappendedre = re.compile(' \n')
127 if ignoreblanklines:
145 if opts.ignoreblanklines:
128 wsblanklinesre = re.compile('\n')
146 wsblanklinesre = re.compile('\n')
129 if ignorews:
147 if opts.ignorews:
130 wsre = re.compile('[ \t]')
148 wsre = re.compile('[ \t]')
131
149
132 # bdiff.blocks gives us the matching sequences in the files. The loop
150 # bdiff.blocks gives us the matching sequences in the files. The loop
@@ -159,13 +177,13 b' def bunidiff(t1, t2, l1, l2, header1, he'
159 if not old and not new:
177 if not old and not new:
160 continue
178 continue
161
179
162 if ignoreblanklines:
180 if opts.ignoreblanklines:
163 wsold = wsblanklinesre.sub('', "".join(old))
181 wsold = wsblanklinesre.sub('', "".join(old))
164 wsnew = wsblanklinesre.sub('', "".join(new))
182 wsnew = wsblanklinesre.sub('', "".join(new))
165 if wsold == wsnew:
183 if wsold == wsnew:
166 continue
184 continue
167
185
168 if ignorewsamount:
186 if opts.ignorewsamount:
169 wsold = wsamountre.sub(' ', "".join(old))
187 wsold = wsamountre.sub(' ', "".join(old))
170 wsold = wsappendedre.sub('\n', wsold)
188 wsold = wsappendedre.sub('\n', wsold)
171 wsnew = wsamountre.sub(' ', "".join(new))
189 wsnew = wsamountre.sub(' ', "".join(new))
@@ -173,7 +191,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
173 if wsold == wsnew:
191 if wsold == wsnew:
174 continue
192 continue
175
193
176 if ignorews:
194 if opts.ignorews:
177 wsold = wsre.sub('', "".join(old))
195 wsold = wsre.sub('', "".join(old))
178 wsnew = wsre.sub('', "".join(new))
196 wsnew = wsre.sub('', "".join(new))
179 if wsold == wsnew:
197 if wsold == wsnew:
@@ -184,7 +202,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
184 prev = None
202 prev = None
185 if hunk:
203 if hunk:
186 # join with the previous hunk if it falls inside the context
204 # join with the previous hunk if it falls inside the context
187 if astart < hunk[1] + context + 1:
205 if astart < hunk[1] + opts.context + 1:
188 prev = hunk
206 prev = hunk
189 astart = hunk[1]
207 astart = hunk[1]
190 bstart = hunk[3]
208 bstart = hunk[3]
@@ -14,7 +14,7 b''
14 allocation of intermediate Python objects. Working memory is about 2x
14 allocation of intermediate Python objects. Working memory is about 2x
15 the total number of hunks.
15 the total number of hunks.
16
16
17 Copyright 2005 Matt Mackall <mpm@selenic.com>
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
18
18
19 This software may be used and distributed according to the terms
19 This software may be used and distributed according to the terms
20 of the GNU General Public License, incorporated herein by reference.
20 of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 """
1 """
2 node.py - basic nodeid manipulation for mercurial
2 node.py - basic nodeid manipulation for mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
@@ -2,7 +2,7 b''
2 # Used for the py2exe distutil.
2 # Used for the py2exe distutil.
3 # This module must be the first mercurial module imported in setup.py
3 # This module must be the first mercurial module imported in setup.py
4 #
4 #
5 # Copyright 2005 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
5 # Copyright 2005, 2006 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
6 #
6 #
7 # This software may be used and distributed according to the terms
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
8 # of the GNU General Public License, incorporated herein by reference.
@@ -1,6 +1,6 b''
1 # remoterepo - remote repositort proxy classes for mercurial
1 # remoterepo - remote repository proxy classes for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -1,6 +1,7 b''
1 # repo.py - repository base classes for mercurial
1 # repo.py - repository base classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
5 #
5 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -4,7 +4,7 b' revlog.py - storage back-end for mercuri'
4 This provides efficient delta storage with O(1) retrieve and append
4 This provides efficient delta storage with O(1) retrieve and append
5 and O(changes) merge between branches
5 and O(changes) merge between branches
6
6
7 Copyright 2005 Matt Mackall <mpm@selenic.com>
7 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License, incorporated herein by reference.
10 of the GNU General Public License, incorporated herein by reference.
@@ -469,7 +469,8 b' class revlog(object):'
469 return self.nodemap[node]
469 return self.nodemap[node]
470 except KeyError:
470 except KeyError:
471 raise RevlogError(_('%s: no node %s') % (self.indexfile, hex(node)))
471 raise RevlogError(_('%s: no node %s') % (self.indexfile, hex(node)))
472 def linkrev(self, node): return self.index[self.rev(node)][-4]
472 def linkrev(self, node):
473 return (node == nullid) and -1 or self.index[self.rev(node)][-4]
473 def parents(self, node):
474 def parents(self, node):
474 if node == nullid: return (nullid, nullid)
475 if node == nullid: return (nullid, nullid)
475 r = self.rev(node)
476 r = self.rev(node)
@@ -743,13 +744,8 b' class revlog(object):'
743
744
744 def lookup(self, id):
745 def lookup(self, id):
745 """locate a node based on revision number or subset of hex nodeid"""
746 """locate a node based on revision number or subset of hex nodeid"""
746 if id in self.nodemap:
747 return id
748 if type(id) == type(0):
747 if type(id) == type(0):
749 rev = id
748 return self.node(id)
750 if rev < 0: rev = self.count() + rev
751 if rev < 0 or rev >= self.count(): return None
752 return self.node(rev)
753 try:
749 try:
754 rev = int(id)
750 rev = int(id)
755 if str(rev) != id: raise ValueError
751 if str(rev) != id: raise ValueError
@@ -762,10 +758,26 b' class revlog(object):'
762 if hex(n).startswith(id):
758 if hex(n).startswith(id):
763 c.append(n)
759 c.append(n)
764 if len(c) > 1: raise RevlogError(_("Ambiguous identifier"))
760 if len(c) > 1: raise RevlogError(_("Ambiguous identifier"))
765 if len(c) < 1: raise RevlogError(_("No match found"))
761 if len(c) == 1: return c[0]
766 return c[0]
762
763 # might need fixing if we change hash lengths
764 if len(id) == 20 and id in self.nodemap:
765 return id
766
767 raise RevlogError(_("No match found"))
767
768
768 return None
769 def cmp(self, node, text):
770 """compare text with a given file revision"""
771 p1, p2 = self.parents(node)
772 return hash(text, p1, p2) != node
773
774 def makenode(self, node, text):
775 """calculate a file nodeid for text, descended or possibly
776 unchanged from node"""
777
778 if self.cmp(node, text):
779 return hash(text, node, nullid)
780 return node
769
781
770 def diff(self, a, b):
782 def diff(self, a, b):
771 """return a delta between two revisions"""
783 """return a delta between two revisions"""
@@ -1,6 +1,6 b''
1 # sshrepo.py - ssh repository proxy class for mercurial
1 # sshrepo.py - ssh repository proxy class for mercurial
2 #
2 #
3 # Copyright 2005 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.
@@ -13,7 +13,7 b' demandload(globals(), "hg os re stat uti'
13
13
14 class sshrepository(remoterepository):
14 class sshrepository(remoterepository):
15 def __init__(self, ui, path, create=0):
15 def __init__(self, ui, path, create=0):
16 self.url = path
16 self._url = path
17 self.ui = ui
17 self.ui = ui
18
18
19 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
19 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
@@ -48,6 +48,9 b' class sshrepository(remoterepository):'
48
48
49 self.validate_repo(ui, sshcmd, args, remotecmd)
49 self.validate_repo(ui, sshcmd, args, remotecmd)
50
50
51 def url(self):
52 return self._url
53
51 def validate_repo(self, ui, sshcmd, args, remotecmd):
54 def validate_repo(self, ui, sshcmd, args, remotecmd):
52 cmd = '%s %s "%s -R %s serve --stdio"'
55 cmd = '%s %s "%s -R %s serve --stdio"'
53 cmd = cmd % (sshcmd, args, remotecmd, self.path)
56 cmd = cmd % (sshcmd, args, remotecmd, self.path)
@@ -180,7 +183,7 b' class sshrepository(remoterepository):'
180 return 1
183 return 1
181 return int(r)
184 return int(r)
182
185
183 def addchangegroup(self, cg, source):
186 def addchangegroup(self, cg, source, url):
184 d = self.call("addchangegroup")
187 d = self.call("addchangegroup")
185 if d:
188 if d:
186 raise hg.RepoError(_("push refused: %s") % d)
189 raise hg.RepoError(_("push refused: %s") % d)
@@ -201,3 +204,5 b' class sshrepository(remoterepository):'
201
204
202 def stream_out(self):
205 def stream_out(self):
203 return self.do_cmd('stream_out')
206 return self.do_cmd('stream_out')
207
208 instance = sshrepository
@@ -1,6 +1,7 b''
1 # sshserver.py - ssh protocol server support for mercurial
1 # sshserver.py - ssh protocol server support for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
5 #
5 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
@@ -117,9 +118,13 b' class sshserver(object):'
117 return
118 return
118
119
119 self.respond("")
120 self.respond("")
120 r = self.repo.addchangegroup(self.fin, 'serve')
121 r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
121 self.respond(str(r))
122 self.respond(str(r))
122
123
124 def client_url(self):
125 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
126 return 'remote:ssh:' + client
127
123 def do_unbundle(self):
128 def do_unbundle(self):
124 their_heads = self.getarg()[1].split()
129 their_heads = self.getarg()[1].split()
125
130
@@ -159,7 +164,7 b' class sshserver(object):'
159 # push can proceed
164 # push can proceed
160
165
161 fp.seek(0)
166 fp.seek(0)
162 r = self.repo.addchangegroup(fp, 'serve')
167 r = self.repo.addchangegroup(fp, 'serve', self.client_url())
163 self.respond(str(r))
168 self.respond(str(r))
164 finally:
169 finally:
165 if not was_locked:
170 if not was_locked:
@@ -2,14 +2,15 b''
2 #
2 #
3 # This provides read-only repo access to repositories exported via static http
3 # This provides read-only repo access to repositories exported via static http
4 #
4 #
5 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
8 # of the GNU General Public License, incorporated herein by reference.
9
9
10 from demandload import demandload
10 from demandload import *
11 from i18n import gettext as _
11 demandload(globals(), "changelog filelog httprangereader")
12 demandload(globals(), "changelog filelog httprangereader")
12 demandload(globals(), "localrepo manifest os urllib urllib2")
13 demandload(globals(), "localrepo manifest os urllib urllib2 util")
13
14
14 class rangereader(httprangereader.httprangereader):
15 class rangereader(httprangereader.httprangereader):
15 def read(self, size=None):
16 def read(self, size=None):
@@ -30,6 +31,7 b' def opener(base):'
30
31
31 class statichttprepository(localrepo.localrepository):
32 class statichttprepository(localrepo.localrepository):
32 def __init__(self, ui, path):
33 def __init__(self, ui, path):
34 self._url = path
33 self.path = (path + "/.hg")
35 self.path = (path + "/.hg")
34 self.ui = ui
36 self.ui = ui
35 self.revlogversion = 0
37 self.revlogversion = 0
@@ -41,8 +43,22 b' class statichttprepository(localrepo.loc'
41 self.encodepats = None
43 self.encodepats = None
42 self.decodepats = None
44 self.decodepats = None
43
45
46 def url(self):
47 return 'static-' + self._url
48
44 def dev(self):
49 def dev(self):
45 return -1
50 return -1
46
51
47 def local(self):
52 def local(self):
48 return False
53 return False
54
55 def instance(ui, path, create):
56 if create:
57 raise util.Abort(_('cannot create new static-http repository'))
58 if path.startswith('old-http:'):
59 ui.warn(_("old-http:// syntax is deprecated, "
60 "please use static-http:// instead\n"))
61 path = path[4:]
62 else:
63 path = path[7:]
64 return statichttprepository(ui, path)
@@ -241,6 +241,7 b' def nl2br(text):'
241 return text.replace('\n', '<br/>\n')
241 return text.replace('\n', '<br/>\n')
242
242
243 def obfuscate(text):
243 def obfuscate(text):
244 text = unicode(text, 'utf-8', 'replace')
244 return ''.join(['&#%d;' % ord(c) for c in text])
245 return ''.join(['&#%d;' % ord(c) for c in text])
245
246
246 def domain(author):
247 def domain(author):
@@ -458,7 +459,7 b' class changeset_templater(object):'
458 yield x
459 yield x
459
460
460 if self.ui.debugflag:
461 if self.ui.debugflag:
461 files = self.repo.changes(log.parents(changenode)[0], changenode)
462 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
462 def showfiles(**args):
463 def showfiles(**args):
463 for x in showlist('file', files[0], **args): yield x
464 for x in showlist('file', files[0], **args): yield x
464 def showadds(**args):
465 def showadds(**args):
@@ -6,7 +6,7 b''
6 # effectively log-structured, this should amount to simply truncating
6 # effectively log-structured, this should amount to simply truncating
7 # anything that isn't referenced in the changelog.
7 # anything that isn't referenced in the changelog.
8 #
8 #
9 # Copyright 2005 Matt Mackall <mpm@selenic.com>
9 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
10 #
10 #
11 # This software may be used and distributed according to the terms
11 # This software may be used and distributed according to the terms
12 # of the GNU General Public License, incorporated herein by reference.
12 # of the GNU General Public License, incorporated herein by reference.
@@ -1,22 +1,24 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005 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 smtplib socket sys tempfile")
10 demandload(globals(), "errno getpass os re socket sys tempfile")
11 demandload(globals(), "ConfigParser 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
@@ -34,6 +36,7 b' class ui(object):'
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
@@ -78,6 +81,8 b' class ui(object):'
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
@@ -169,17 +174,6 b' class ui(object):'
169 result[key.lower()] = value
174 result[key.lower()] = value
170 return result
175 return result
171
176
172 def diffopts(self):
173 if self.diffcache:
174 return self.diffcache
175 result = {'showfunc': True, 'ignorews': False,
176 'ignorewsamount': False, 'ignoreblanklines': False}
177 for key, value in self.configitems("diff"):
178 if value:
179 result[key.lower()] = (value.lower() == 'true')
180 self.diffcache = result
181 return result
182
183 def username(self):
177 def username(self):
184 """Return default username to be used in commits.
178 """Return default username to be used in commits.
185
179
@@ -197,7 +191,7 b' class ui(object):'
197 user = os.environ.get("EMAIL")
191 user = os.environ.get("EMAIL")
198 if user is None:
192 if user is None:
199 try:
193 try:
200 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
194 user = '%s@%s' % (util.getuser(), socket.getfqdn())
201 except KeyError:
195 except KeyError:
202 raise util.Abort(_("Please specify a username."))
196 raise util.Abort(_("Please specify a username."))
203 return user
197 return user
@@ -217,12 +211,6 b' class ui(object):'
217 path = self.config("paths", default)
211 path = self.config("paths", default)
218 return path or loc
212 return path or loc
219
213
220 def setconfig_remoteopts(self, **opts):
221 if opts.get('ssh'):
222 self.setconfig("ui", "ssh", opts['ssh'])
223 if opts.get('remotecmd'):
224 self.setconfig("ui", "remotecmd", opts['remotecmd'])
225
226 def write(self, *args):
214 def write(self, *args):
227 if self.header:
215 if self.header:
228 if self.header != self.prev_header:
216 if self.header != self.prev_header:
@@ -298,62 +286,6 b' class ui(object):'
298
286
299 return t
287 return t
300
288
301 def sendmail(self):
302 '''send mail message. object returned has one method, sendmail.
303 call as sendmail(sender, list-of-recipients, msg).'''
304
305 def smtp():
306 '''send mail using smtp.'''
307
308 local_hostname = self.config('smtp', 'local_hostname')
309 s = smtplib.SMTP(local_hostname=local_hostname)
310 mailhost = self.config('smtp', 'host')
311 if not mailhost:
312 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
313 mailport = int(self.config('smtp', 'port', 25))
314 self.note(_('sending mail: smtp host %s, port %s\n') %
315 (mailhost, mailport))
316 s.connect(host=mailhost, port=mailport)
317 if self.configbool('smtp', 'tls'):
318 self.note(_('(using tls)\n'))
319 s.ehlo()
320 s.starttls()
321 s.ehlo()
322 username = self.config('smtp', 'username')
323 password = self.config('smtp', 'password')
324 if username and password:
325 self.note(_('(authenticating to mail server as %s)\n') %
326 (username))
327 s.login(username, password)
328 return s
329
330 class sendmail(object):
331 '''send mail using sendmail.'''
332
333 def __init__(self, ui, program):
334 self.ui = ui
335 self.program = program
336
337 def sendmail(self, sender, recipients, msg):
338 cmdline = '%s -f %s %s' % (
339 self.program, templater.email(sender),
340 ' '.join(map(templater.email, recipients)))
341 self.ui.note(_('sending mail: %s\n') % cmdline)
342 fp = os.popen(cmdline, 'w')
343 fp.write(msg)
344 ret = fp.close()
345 if ret:
346 raise util.Abort('%s %s' % (
347 os.path.basename(self.program.split(None, 1)[0]),
348 util.explain_exit(ret)[0]))
349
350 method = self.config('email', 'method', 'smtp')
351 if method == 'smtp':
352 mail = smtp()
353 else:
354 mail = sendmail(self, method)
355 return mail
356
357 def print_exc(self):
289 def print_exc(self):
358 '''print exception traceback if traceback printing enabled.
290 '''print exception traceback if traceback printing enabled.
359 only to call in exception handler. returns true if traceback
291 only to call in exception handler. returns true if traceback
@@ -2,6 +2,8 b''
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5
7
6 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
@@ -12,7 +14,7 b' platform-specific details from the core.'
12
14
13 from i18n import gettext as _
15 from i18n import gettext as _
14 from demandload import *
16 from demandload import *
15 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
16 demandload(globals(), "os threading time")
18 demandload(globals(), "os threading time")
17
19
18 # used by parsedate
20 # used by parsedate
@@ -93,23 +95,6 b' def find_in_path(name, path, default=Non'
93 return p_name
95 return p_name
94 return default
96 return default
95
97
96 def patch(strip, patchname, ui):
97 """apply the patch <patchname> to the working directory.
98 a list of patched files is returned"""
99 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
100 fp = os.popen('%s -p%d < "%s"' % (patcher, strip, patchname))
101 files = {}
102 for line in fp:
103 line = line.rstrip()
104 ui.status("%s\n" % line)
105 if line.startswith('patching file '):
106 pf = parse_patch_output(line)
107 files.setdefault(pf, 1)
108 code = fp.close()
109 if code:
110 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
111 return files.keys()
112
113 def binary(s):
98 def binary(s):
114 """return true if a string is binary data using diff's heuristic"""
99 """return true if a string is binary data using diff's heuristic"""
115 if s and '\0' in s[:4096]:
100 if s and '\0' in s[:4096]:
@@ -510,6 +495,20 b' def is_win_9x():'
510 except AttributeError:
495 except AttributeError:
511 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
496 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
512
497
498 getuser_fallback = None
499
500 def getuser():
501 '''return name of current user'''
502 try:
503 return getpass.getuser()
504 except ImportError:
505 # import of pwd will fail on windows - try fallback
506 if getuser_fallback:
507 return getuser_fallback()
508 # raised if win32api not available
509 raise Abort(_('user name not available - set USERNAME '
510 'environment variable'))
511
513 # Platform specific variants
512 # Platform specific variants
514 if os.name == 'nt':
513 if os.name == 'nt':
515 demandload(globals(), "msvcrt")
514 demandload(globals(), "msvcrt")
@@ -593,6 +592,9 b" if os.name == 'nt':"
593 def samestat(s1, s2):
592 def samestat(s1, s2):
594 return False
593 return False
595
594
595 def shellquote(s):
596 return '"%s"' % s.replace('"', '\\"')
597
596 def explain_exit(code):
598 def explain_exit(code):
597 return _("exited with status %d") % code, code
599 return _("exited with status %d") % code, code
598
600
@@ -682,6 +684,9 b' else:'
682 else:
684 else:
683 raise
685 raise
684
686
687 def shellquote(s):
688 return "'%s'" % s.replace("'", "'\\''")
689
685 def testpid(pid):
690 def testpid(pid):
686 '''return False if pid dead, True if running or not sure'''
691 '''return False if pid dead, True if running or not sure'''
687 try:
692 try:
@@ -982,3 +987,11 b' def bytecount(nbytes):'
982 if nbytes >= divisor * multiplier:
987 if nbytes >= divisor * multiplier:
983 return format % (nbytes / float(divisor))
988 return format % (nbytes / float(divisor))
984 return units[-1][2] % nbytes
989 return units[-1][2] % nbytes
990
991 def drop_scheme(scheme, path):
992 sc = scheme + ':'
993 if path.startswith(sc):
994 path = path[len(sc):]
995 if path.startswith('//'):
996 path = path[2:]
997 return path
@@ -297,3 +297,5 b' class posixfile_nt(object):'
297 win32file.SetEndOfFile(self.handle)
297 win32file.SetEndOfFile(self.handle)
298 except pywintypes.error, err:
298 except pywintypes.error, err:
299 raise WinIOError(err)
299 raise WinIOError(err)
300
301 getuser_fallback = win32api.GetUserName
@@ -1,4 +1,4 b''
1 # Copyright (C) 2005 by Intevation GmbH
1 # Copyright (C) 2005, 2006 by Intevation GmbH
2 # Author(s):
2 # Author(s):
3 # Thomas Arendsen Hein <thomas@intevation.de>
3 # Thomas Arendsen Hein <thomas@intevation.de>
4 #
4 #
@@ -20,7 +20,7 b''
20 </div>
20 </div>
21
21
22 <div class="page_nav">
22 <div class="page_nav">
23 <a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
23 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;rev=#rev#;style=gitweb">shortlog</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/>
24 <br/>
24 <br/>
25 #changenav%naventry#<br/>
25 #changenav%naventry#<br/>
26 </div>
26 </div>
@@ -6,6 +6,7 b''
6 <body>
6 <body>
7
7
8 <div class="buttons">
8 <div class="buttons">
9 <a href="?sl=#rev#">shortlog</a>
9 <a href="?cmd=tags">tags</a>
10 <a href="?cmd=tags">tags</a>
10 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 #archives%archiveentry#
12 #archives%archiveentry#
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a> #archives%archiveentry#<br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;rev=#rev#;style=gitweb">shortlog</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a> #archives%archiveentry#<br/>
14 </div>
14 </div>
15
15
16 <div>
16 <div>
@@ -5,6 +5,7 b''
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="?cl=#rev#">changelog</a>
7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 <a href="?cmd=tags">tags</a>
9 <a href="?cmd=tags">tags</a>
9 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 <a href="?cs=#node|short#;style=raw">raw</a>
11 <a href="?cs=#node|short#;style=raw">raw</a>
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
14 </div>
14 </div>
15
15
16 <div>
16 <div>
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | annotate | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | annotate | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
14 </div>
14 </div>
15
15
16 <div class="title">#file|escape#</div>
16 <div class="title">#file|escape#</div>
@@ -5,6 +5,7 b''
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="?cl=#rev#">changelog</a>
7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 <a href="?tags=">tags</a>
9 <a href="?tags=">tags</a>
9 <a href="?cs=#node|short#">changeset</a>
10 <a href="?cs=#node|short#">changeset</a>
10 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
11 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
@@ -5,6 +5,7 b''
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="?cl=#rev#">changelog</a>
7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 <a href="?tags=">tags</a>
9 <a href="?tags=">tags</a>
9 <a href="?cs=#node|short#">changeset</a>
10 <a href="?cs=#node|short#">changeset</a>
10 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
11 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | revisions | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=rss">rss</a><br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | revisions | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=rss">rss</a><br/>
14 </div>
14 </div>
15
15
16 <div class="title" >#file|urlescape#</div>
16 <div class="title" >#file|urlescape#</div>
@@ -8,6 +8,7 b''
8
8
9 <div class="buttons">
9 <div class="buttons">
10 <a href="?cl=tip">changelog</a>
10 <a href="?cl=tip">changelog</a>
11 <a href="?sl=tip">shortlog</a>
11 <a href="?tags=">tags</a>
12 <a href="?tags=">tags</a>
12 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
13 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
13 <a href="?fa=#filenode|short#;file=#file|urlescape#">annotate</a>
14 <a href="?fa=#filenode|short#;file=#file|urlescape#">annotate</a>
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
14 </div>
14 </div>
15
15
16 <div class="title">#file|escape#</div>
16 <div class="title">#file|escape#</div>
@@ -5,6 +5,7 b''
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="?cl=#rev#">changelog</a>
7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 <a href="?tags=">tags</a>
9 <a href="?tags=">tags</a>
9 <a href="?cs=#node|short#">changeset</a>
10 <a href="?cs=#node|short#">changeset</a>
10 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
11 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | manifest | <a href="?cs=#node|short#;style=gitweb">changeset</a> #archives%archiveentry#<br/>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | manifest | <a href="?cs=#node|short#;style=gitweb">changeset</a> #archives%archiveentry#<br/>
14 </div>
14 </div>
15
15
16 <div class="title" >#path|escape#</div>
16 <div class="title" >#path|escape#</div>
@@ -5,6 +5,7 b''
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="?cl=#rev#">changelog</a>
7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 <a href="?tags=">tags</a>
9 <a href="?tags=">tags</a>
9 <a href="?cs=#node|short#">changeset</a>
10 <a href="?cs=#node|short#">changeset</a>
10 #archives%archiveentry#
11 #archives%archiveentry#
@@ -3,7 +3,10 b' header = header.tmpl'
3 footer = footer.tmpl
3 footer = footer.tmpl
4 search = search.tmpl
4 search = search.tmpl
5 changelog = changelog.tmpl
5 changelog = changelog.tmpl
6 shortlog = shortlog.tmpl
7 shortlogentry = shortlogentry.tmpl
6 naventry = '<a href="?cl=#rev#">#label|escape#</a> '
8 naventry = '<a href="?cl=#rev#">#label|escape#</a> '
9 navshortentry = '<a href="?sl=#rev#">#label|escape#</a> '
7 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
10 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
8 filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> '
11 filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> '
9 fileellipses = '...'
12 fileellipses = '...'
@@ -1,6 +1,6 b''
1 #header#
1 #header#
2 <div class="page_nav">
2 <div class="page_nav">
3 <a href="?cmd=summary;style=gitweb">summary</a> | log | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
3 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
4 </div>
4 </div>
5
5
6 <h2>searching for #query|escape#</h2>
6 <h2>searching for #query|escape#</h2>
@@ -5,6 +5,7 b''
5
5
6 <div class="buttons">
6 <div class="buttons">
7 <a href="?cl=tip">changelog</a>
7 <a href="?cl=tip">changelog</a>
8 <a href="?sl=tip">shortlog</a>
8 <a href="?tags=">tags</a>
9 <a href="?tags=">tags</a>
9 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 </div>
11 </div>
@@ -1,13 +1,32 b''
1 #header#
1 #header#
2 <title>#repo|escape#: Shortlog</title>
3 <link rel="alternate" type="application/rss+xml"
4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 </head>
6 <body>
2
7
8 <div class="page_header">
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / shortlog
10 </div>
11
12 <form action="#">
13 <div class="search">
14 <input type="hidden" name="repo" value="#repo|escape#" />
15 <input type="hidden" name="style" value="gitweb" />
16 <input type="hidden" name="cmd" value="changelog" />
17 <input type="text" name="rev" />
18 </div>
19 </form>
20 </div>
3 <div class="page_nav">
21 <div class="page_nav">
4 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
22 <a href="?cmd=summary;style=gitweb">summary</a> | shortlog | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/>
23 <br/>
5
24
6 #changenav%naventry#<br/>
25 #changenav%navshortentry#<br/>
7 </div>
26 </div>
8
27
9 <table cellspacing="0">
28 <table cellspacing="0">
10 #entries#
29 #entries%shortlogentry#
11 </table>
30 </table>
12
31
13 #footer#
32 #footer#
@@ -57,6 +57,12 b' pre { margin: 0; }'
57 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
57 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
58 .logEntry th.firstline { text-align: left; width: inherit; }
58 .logEntry th.firstline { text-align: left; width: inherit; }
59
59
60 /* Shortlog entries */
61 .slogEntry { width: 100%; font-size: smaller; }
62 .slogEntry .age { width: 7%; }
63 .slogEntry td { font-weight: normal; text-align: left; vertical-align: top; }
64 .slogEntry td.author { width: 35%; }
65
60 /* Tag entries */
66 /* Tag entries */
61 #tagEntries { list-style: none; margin: 0; padding: 0; }
67 #tagEntries { list-style: none; margin: 0; padding: 0; }
62 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
68 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
@@ -9,7 +9,8 b''
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary
9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary
10 </div>
10 </div>
11 <div class="page_nav">
11 <div class="page_nav">
12 summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
12 summary | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#
13 <br/>
13 </div>
14 </div>
14
15
15 <div class="title">&nbsp;</div>
16 <div class="title">&nbsp;</div>
@@ -10,7 +10,7 b''
10 </div>
10 </div>
11
11
12 <div class="page_nav">
12 <div class="page_nav">
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | tags | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>
13 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | tags | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>
14 <br/>
14 <br/>
15 </div>
15 </div>
16
16
@@ -7,6 +7,7 b''
7
7
8 <div class="buttons">
8 <div class="buttons">
9 <a href="?cl=tip">changelog</a>
9 <a href="?cl=tip">changelog</a>
10 <a href="?sl=tip">shortlog</a>
10 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 <a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a>
12 <a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a>
12 </div>
13 </div>
@@ -28,6 +28,6 b' writing tests:'
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
@@ -25,7 +25,7 b' parser = optparse.OptionParser("%prog [o'
25 parser.add_option("-v", "--verbose", action="store_true",
25 parser.add_option("-v", "--verbose", action="store_true",
26 help="output verbose messages")
26 help="output verbose messages")
27 parser.add_option("-t", "--timeout", type="int",
27 parser.add_option("-t", "--timeout", type="int",
28 help="output verbose messages")
28 help="kill errant tests after TIMEOUT seconds")
29 parser.add_option("-c", "--cover", action="store_true",
29 parser.add_option("-c", "--cover", action="store_true",
30 help="print a test coverage report")
30 help="print a test coverage report")
31 parser.add_option("-s", "--cover_stdlib", action="store_true",
31 parser.add_option("-s", "--cover_stdlib", action="store_true",
@@ -201,6 +201,11 b' def run(cmd):'
201 return ret, splitnewlines(output)
201 return ret, splitnewlines(output)
202
202
203 def run_one(test):
203 def run_one(test):
204 '''tristate output:
205 None -> skipped
206 True -> passed
207 False -> failed'''
208
204 vlog("# Test", test)
209 vlog("# Test", test)
205 if not verbose:
210 if not verbose:
206 sys.stdout.write('.')
211 sys.stdout.write('.')
@@ -217,15 +222,28 b' def run_one(test):'
217 os.mkdir(tmpd)
222 os.mkdir(tmpd)
218 os.chdir(tmpd)
223 os.chdir(tmpd)
219
224
220 if test.endswith(".py"):
225 lctest = test.lower()
221 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
222 else:
223 cmd = '"%s"' % (os.path.join(TESTDIR, test))
224
226
225 # To reliably get the error code from batch files on WinXP,
227 if lctest.endswith('.py'):
226 # the "cmd /c call" prefix is needed. Grrr
228 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
227 if os.name == 'nt' and test.endswith(".bat"):
229 elif lctest.endswith('.bat'):
230 # do not run batch scripts on non-windows
231 if os.name != 'nt':
232 print '\nSkipping %s: batch script' % test
233 return None
234 # To reliably get the error code from batch files on WinXP,
235 # the "cmd /c call" prefix is needed. Grrr
228 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
236 cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
237 else:
238 # do not run shell scripts on windows
239 if os.name == 'nt':
240 print '\nSkipping %s: shell script' % test
241 return None
242 # do not try to run non-executable programs
243 if not os.access(os.path.join(TESTDIR, test), os.X_OK):
244 print '\nSkipping %s: not executable' % test
245 return None
246 cmd = '"%s"' % (os.path.join(TESTDIR, test))
229
247
230 if options.timeout > 0:
248 if options.timeout > 0:
231 signal.alarm(options.timeout)
249 signal.alarm(options.timeout)
@@ -244,7 +262,7 b' def run_one(test):'
244 ref_out = splitnewlines(f.read())
262 ref_out = splitnewlines(f.read())
245 f.close()
263 f.close()
246 else:
264 else:
247 ref_out = ['']
265 ref_out = []
248 if out != ref_out:
266 if out != ref_out:
249 diffret = 1
267 diffret = 1
250 print "\nERROR: %s output changed" % (test)
268 print "\nERROR: %s output changed" % (test)
@@ -330,16 +348,23 b' try:'
330
348
331 tests = 0
349 tests = 0
332 failed = 0
350 failed = 0
351 skipped = 0
333
352
334 if len(args) == 0:
353 if len(args) == 0:
335 args = os.listdir(".")
354 args = os.listdir(".")
336 for test in args:
355 for test in args:
337 if test.startswith("test-") and not '~' in test and not '.' in test:
356 if (test.startswith("test-") and '~' not in test and
338 if not run_one(test):
357 ('.' not in test or test.endswith('.py') or
358 test.endswith('.bat'))):
359 ret = run_one(test)
360 if ret is None:
361 skipped += 1
362 elif not ret:
339 failed += 1
363 failed += 1
340 tests += 1
364 tests += 1
341
365
342 print "\n# Ran %d tests, %d failed." % (tests, failed)
366 print "\n# Ran %d tests, %d skipped, %d failed." % (tests, skipped,
367 failed)
343 if coverage:
368 if coverage:
344 output_coverage()
369 output_coverage()
345 except KeyboardInterrupt:
370 except KeyboardInterrupt:
@@ -27,7 +27,7 b' adding b'
27 reverting a
27 reverting a
28 changeset 3:4cbb1e70196a backs out changeset 1:22bca4c721e5
28 changeset 3:4cbb1e70196a backs out changeset 1:22bca4c721e5
29 the backout changeset is a new head - do not forget to merge
29 the backout changeset is a new head - do not forget to merge
30 (use "backout -m" if you want to auto-merge)
30 (use "backout --merge" if you want to auto-merge)
31 b: No such file or directory
31 b: No such file or directory
32 adding a
32 adding a
33 adding b
33 adding b
@@ -30,14 +30,20 b' cd ..'
30 hg init empty
30 hg init empty
31 hg -R test bundle full.hg empty
31 hg -R test bundle full.hg empty
32 hg -R test unbundle full.hg
32 hg -R test unbundle full.hg
33 hg -R empty unbundle full.hg
34 hg -R empty heads
33 hg -R empty heads
35 hg -R empty verify
34 hg -R empty verify
36
35
36 hg --cwd test pull ../full.hg
37 hg --cwd empty pull ../full.hg
38 hg -R empty rollback
39 hg --cwd empty pull ../full.hg
40
37 rm -rf empty
41 rm -rf empty
38 hg init empty
42 hg init empty
39 cd empty
43 cd empty
40 hg -R bundle://../full.hg log
44 hg -R bundle://../full.hg log
45 echo '[hooks]' >> .hg/hgrc
46 echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
41 #doesn't work (yet ?)
47 #doesn't work (yet ?)
42 #hg -R bundle://../full.hg verify
48 #hg -R bundle://../full.hg verify
43 hg pull bundle://../full.hg
49 hg pull bundle://../full.hg
@@ -11,28 +11,34 b' adding manifests'
11 adding file changes
11 adding file changes
12 added 0 changesets with 0 changes to 4 files
12 added 0 changesets with 0 changes to 4 files
13 (run 'hg update' to get a working copy)
13 (run 'hg update' to get a working copy)
14 changeset: -1:000000000000
15 tag: tip
16 user:
17 date: Thu Jan 01 00:00:00 1970 +0000
18
19 checking changesets
20 checking manifests
21 crosschecking files in changesets and manifests
22 checking files
23 0 files, 0 changesets, 0 total revisions
24 pulling from ../full.hg
25 searching for changes
26 no changes found
27 pulling from ../full.hg
28 requesting all changes
14 adding changesets
29 adding changesets
15 adding manifests
30 adding manifests
16 adding file changes
31 adding file changes
17 added 9 changesets with 7 changes to 4 files (+1 heads)
32 added 9 changesets with 7 changes to 4 files (+1 heads)
18 (run 'hg heads' to see heads, 'hg merge' to merge)
33 (run 'hg heads' to see heads, 'hg merge' to merge)
19 changeset: 8:836ac62537ab
34 rolling back last transaction
20 tag: tip
35 pulling from ../full.hg
21 parent: 3:ac69c658229d
36 requesting all changes
22 user: test
37 adding changesets
23 date: Mon Jan 12 13:46:40 1970 +0000
38 adding manifests
24 summary: 0.3m
39 adding file changes
25
40 added 9 changesets with 7 changes to 4 files (+1 heads)
26 changeset: 7:80fe151401c2
41 (run 'hg heads' to see heads, 'hg merge' to merge)
27 user: test
28 date: Mon Jan 12 13:46:40 1970 +0000
29 summary: 1.3m
30
31 checking changesets
32 checking manifests
33 crosschecking files in changesets and manifests
34 checking files
35 4 files, 9 changesets, 7 total revisions
36 changeset: 8:836ac62537ab
42 changeset: 8:836ac62537ab
37 tag: tip
43 tag: tip
38 parent: 3:ac69c658229d
44 parent: 3:ac69c658229d
@@ -81,6 +87,7 b' user: test'
81 date: Mon Jan 12 13:46:40 1970 +0000
87 date: Mon Jan 12 13:46:40 1970 +0000
82 summary: 0.0
88 summary: 0.0
83
89
90 changegroup: u=bundle:../full.hg
84 pulling from bundle://../full.hg
91 pulling from bundle://../full.hg
85 requesting all changes
92 requesting all changes
86 adding changesets
93 adding changesets
@@ -45,7 +45,7 b' hg --cwd c head -v'
45 hg --cwd b tip --verbose
45 hg --cwd b tip --verbose
46
46
47 echo %% --config
47 echo %% --config
48 hg --cwd c --config paths.quuxfoo=bar paths | grep -q quuxfoo && echo quuxfoo
48 hg --cwd c --config paths.quuxfoo=bar paths | grep quuxfoo > /dev/null && echo quuxfoo
49 hg --cwd c --config '' tip -q
49 hg --cwd c --config '' tip -q
50 hg --cwd c --config a.b tip -q
50 hg --cwd c --config a.b tip -q
51 hg --cwd c --config a tip -q
51 hg --cwd c --config a tip -q
@@ -18,6 +18,13 b' head -n 3 port > port1'
18 mv port1 port
18 mv port1 port
19 hg commit -m 4 -u spam -d '4 0'
19 hg commit -m 4 -u spam -d '4 0'
20 hg grep port port
20 hg grep port port
21 echo 'FIXME: history is wrong here'
22 hg grep --all -nu port port
21 hg grep --all -nu port port
23 hg grep import port
22 hg grep import port
23
24 hg cp port port2
25 hg commit -m 4 -u spam -d '5 0'
26 echo '% follow'
27 hg grep -f 'import$' port2
28 echo deport >> port2
29 hg commit -m 5 -u eggs -d '6 0'
30 hg grep -f --all -nu port port2
@@ -1,10 +1,25 b''
1 port:4:export
1 port:4:export
2 port:4:vaportight
2 port:4:vaportight
3 port:4:import/export
3 port:4:import/export
4 FIXME: history is wrong here
4 port:4:4:-:spam:import/export
5 port:1:1:-:eggs:import
5 port:3:4:+:eggs:import/export
6 port:1:2:+:eggs:vaportight
6 port:2:1:-:spam:import
7 port:1:3:+:eggs:import/export
7 port:2:2:-:spam:export
8 port:0:2:+:spam:export
8 port:2:1:+:spam:export
9 port:0:1:+:spam:import
9 port:2:2:+:spam:vaportight
10 port:2:3:+:spam:import/export
11 port:1:2:+:eggs:export
12 port:0:1:+:eggs:import
10 port:4:import/export
13 port:4:import/export
14 % follow
15 port:0:import
16 port2:6:4:+:eggs:deport
17 port:4:4:-:spam:import/export
18 port:3:4:+:eggs:import/export
19 port:2:1:-:spam:import
20 port:2:2:-:spam:export
21 port:2:1:+:spam:export
22 port:2:2:+:spam:vaportight
23 port:2:3:+:spam:import/export
24 port:1:2:+:eggs:export
25 port:0:1:+:eggs:import
@@ -178,6 +178,7 b' options:'
178 -r --rev revision
178 -r --rev revision
179 -a --text treat all files as text
179 -a --text treat all files as text
180 -p --show-function show which function each change is in
180 -p --show-function show which function each change is in
181 -g --git use git extended diff format
181 -w --ignore-all-space ignore white space when comparing lines
182 -w --ignore-all-space ignore white space when comparing lines
182 -b --ignore-space-change ignore changes in the amount of white space
183 -b --ignore-space-change ignore changes in the amount of white space
183 -B --ignore-blank-lines ignore changes whose lines are all blank
184 -B --ignore-blank-lines ignore changes whose lines are all blank
@@ -187,13 +188,15 b' hg status [OPTION]... [FILE]...'
187
188
188 show changed files in the working directory
189 show changed files in the working directory
189
190
190 Show changed files in the repository. If names are
191 Show status of files in the repository. If names are given, only
191 given, only files that match are shown.
192 files that match are shown. Files that are clean or ignored, are
193 not listed unless -c (clean), -i (ignored) or -A is given.
192
194
193 The codes used to show the status of files are:
195 The codes used to show the status of files are:
194 M = modified
196 M = modified
195 A = added
197 A = added
196 R = removed
198 R = removed
199 C = clean
197 ! = deleted, but still tracked
200 ! = deleted, but still tracked
198 ? = not tracked
201 ? = not tracked
199 I = ignored (not shown by default)
202 I = ignored (not shown by default)
@@ -203,10 +206,12 b' aliases: st'
203
206
204 options:
207 options:
205
208
209 -A --all show status of all files
206 -m --modified show only modified files
210 -m --modified show only modified files
207 -a --added show only added files
211 -a --added show only added files
208 -r --removed show only removed files
212 -r --removed show only removed files
209 -d --deleted show only deleted (but tracked) files
213 -d --deleted show only deleted (but tracked) files
214 -c --clean show only files without changes
210 -u --unknown show only unknown (not tracked) files
215 -u --unknown show only unknown (not tracked) files
211 -i --ignored show ignored files
216 -i --ignored show ignored files
212 -n --no-status hide status prefix
217 -n --no-status hide status prefix
@@ -17,9 +17,9 b' cd ../b'
17
17
18 # changegroup hooks can see env vars
18 # changegroup hooks can see env vars
19 echo '[hooks]' > .hg/hgrc
19 echo '[hooks]' > .hg/hgrc
20 echo 'prechangegroup = echo prechangegroup hook' >> .hg/hgrc
20 echo 'prechangegroup = echo prechangegroup hook: u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
21 echo 'changegroup = echo changegroup hook: n=$HG_NODE' >> .hg/hgrc
21 echo 'changegroup = echo changegroup hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
22 echo 'incoming = echo incoming hook: n=$HG_NODE' >> .hg/hgrc
22 echo 'incoming = echo incoming hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
23
23
24 # pretxncommit and commit hooks can see both parents of merge
24 # pretxncommit and commit hooks can see both parents of merge
25 cd ../a
25 cd ../a
@@ -22,11 +22,11 b' pretxncommit hook: n=4c52fb2e402287dd5dc'
22 3:4c52fb2e4022
22 3:4c52fb2e4022
23 commit hook: n=4c52fb2e402287dd5dc052090682536c8406c321 p1=1324a5531bac09b329c3845d35ae6a7526874edb p2=b702efe9688826e3a91283852b328b84dbf37bc2
23 commit hook: n=4c52fb2e402287dd5dc052090682536c8406c321 p1=1324a5531bac09b329c3845d35ae6a7526874edb p2=b702efe9688826e3a91283852b328b84dbf37bc2
24 commit hook b
24 commit hook b
25 prechangegroup hook
25 prechangegroup hook: u=file:
26 changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2
26 changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file:
27 incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2
27 incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file:
28 incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb
28 incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb u=file:
29 incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321
29 incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321 u=file:
30 pulling from ../a
30 pulling from ../a
31 searching for changes
31 searching for changes
32 adding changesets
32 adding changesets
@@ -4,22 +4,31 b' hg init test'
4 cd test
4 cd test
5 echo foo>foo
5 echo foo>foo
6 hg commit -A -d '0 0' -m 1
6 hg commit -A -d '0 0' -m 1
7 hg --config server.uncompressed=True serve -p 20059 -d --pid-file=hg1.pid
7 hg --config server.uncompressed=True serve -p 20059 -d --pid-file=../hg1.pid
8 cat hg1.pid >> $DAEMON_PIDS
8 hg serve -p 20060 -d --pid-file=../hg2.pid
9 hg serve -p 20060 -d --pid-file=hg2.pid
10 cat hg2.pid >> $DAEMON_PIDS
11 cd ..
9 cd ..
10 cat hg1.pid hg2.pid >> $DAEMON_PIDS
12
11
13 echo % clone via stream
12 echo % clone via stream
14 http_proxy= hg clone --uncompressed http://localhost:20059/ copy 2>&1 | \
13 http_proxy= hg clone --uncompressed http://localhost:20059/ copy 2>&1 | \
15 sed -e 's/[0-9][0-9.]*/XXX/g'
14 sed -e 's/[0-9][0-9.]*/XXX/g'
16 cd copy
15 hg verify -R copy
17 hg verify
18
16
19 echo % try to clone via stream, should use pull instead
17 echo % try to clone via stream, should use pull instead
20 http_proxy= hg clone --uncompressed http://localhost:20060/ copy2
18 http_proxy= hg clone --uncompressed http://localhost:20060/ copy2
21
19
22 echo % clone via pull
20 echo % clone via pull
23 http_proxy= hg clone http://localhost:20059/ copy-pull
21 http_proxy= hg clone http://localhost:20059/ copy-pull
22 hg verify -R copy-pull
23
24 cd test
25 echo bar > bar
26 hg commit -A -d '1 0' -m 2
27 cd ..
28
29 echo % pull
24 cd copy-pull
30 cd copy-pull
25 hg verify
31 echo '[hooks]' >> .hg/hgrc
32 echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
33 hg pull
34 cd ..
@@ -28,3 +28,13 b' checking manifests'
28 crosschecking files in changesets and manifests
28 crosschecking files in changesets and manifests
29 checking files
29 checking files
30 1 files, 1 changesets, 1 total revisions
30 1 files, 1 changesets, 1 total revisions
31 adding bar
32 % pull
33 changegroup: u=http://localhost:20059/
34 pulling from http://localhost:20059/
35 searching for changes
36 adding changesets
37 adding manifests
38 adding file changes
39 added 1 changesets with 1 changes to 1 files
40 (run 'hg update' to get a working copy)
@@ -1,7 +1,10 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 mkdir a/d1
5 mkdir a/d1/d2
4 echo line 1 > a/a
6 echo line 1 > a/a
7 echo line 1 > a/d1/d2/a
5 hg --cwd a ci -d '0 0' -Ama
8 hg --cwd a ci -d '0 0' -Ama
6
9
7 echo line 2 >> a/a
10 echo line 2 >> a/a
@@ -69,7 +72,7 b' rm -rf b'
69
72
70 echo % plain diff in email, no subject, no message body, should fail
73 echo % plain diff in email, no subject, no message body, should fail
71 hg clone -r0 a b
74 hg clone -r0 a b
72 grep -v '^\(Subject\|email\)' msg.patch | hg --cwd b import -
75 egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
73 rm -rf b
76 rm -rf b
74
77
75 echo % hg export in email, should use patch header
78 echo % hg export in email, should use patch header
@@ -79,3 +82,20 b' python mkmsg.py | hg --cwd b import -'
79 hg --cwd b tip | grep second
82 hg --cwd b tip | grep second
80 rm -rf b
83 rm -rf b
81
84
85 # bug non regression test
86 # importing a patch in a subdirectory failed at the commit stage
87 echo line 2 >> a/d1/d2/a
88 hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
89 echo % hg import in a subdirectory
90 hg clone -r0 a b
91 hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch
92 dir=`pwd`
93 cd b/d1/d2 2>&1 > /dev/null
94 hg import ../../../tip.patch
95 cd $dir
96 echo "% message should be 'subdir change'"
97 hg --cwd b tip | grep 'subdir change'
98 echo "% committer should be 'someoneelse'"
99 hg --cwd b tip | grep someoneelse
100 echo "% should be empty"
101 hg --cwd b status
@@ -1,13 +1,13 b''
1 adding a
1 adding a
2 adding d1/d2/a
2 % import exported patch
3 % import exported patch
3 requesting all changes
4 requesting all changes
4 adding changesets
5 adding changesets
5 adding manifests
6 adding manifests
6 adding file changes
7 adding file changes
7 added 1 changesets with 1 changes to 1 files
8 added 1 changesets with 2 changes to 2 files
8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 applying ../tip.patch
10 applying ../tip.patch
10 patching file a
11 % message should be same
11 % message should be same
12 summary: second change
12 summary: second change
13 % committer should be same
13 % committer should be same
@@ -17,10 +17,9 b' requesting all changes'
17 adding changesets
17 adding changesets
18 adding manifests
18 adding manifests
19 adding file changes
19 adding file changes
20 added 1 changesets with 1 changes to 1 files
20 added 1 changesets with 2 changes to 2 files
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 applying ../tip.patch
22 applying ../tip.patch
23 patching file a
24 transaction abort!
23 transaction abort!
25 rollback completed
24 rollback completed
26 % import of plain diff should be ok with message
25 % import of plain diff should be ok with message
@@ -28,38 +27,34 b' requesting all changes'
28 adding changesets
27 adding changesets
29 adding manifests
28 adding manifests
30 adding file changes
29 adding file changes
31 added 1 changesets with 1 changes to 1 files
30 added 1 changesets with 2 changes to 2 files
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 applying ../tip.patch
32 applying ../tip.patch
34 patching file a
35 % import from stdin
33 % import from stdin
36 requesting all changes
34 requesting all changes
37 adding changesets
35 adding changesets
38 adding manifests
36 adding manifests
39 adding file changes
37 adding file changes
40 added 1 changesets with 1 changes to 1 files
38 added 1 changesets with 2 changes to 2 files
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
39 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 applying patch from stdin
40 applying patch from stdin
43 patching file a
44 % override commit message
41 % override commit message
45 requesting all changes
42 requesting all changes
46 adding changesets
43 adding changesets
47 adding manifests
44 adding manifests
48 adding file changes
45 adding file changes
49 added 1 changesets with 1 changes to 1 files
46 added 1 changesets with 2 changes to 2 files
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 applying patch from stdin
48 applying patch from stdin
52 patching file a
53 summary: override
49 summary: override
54 % plain diff in email, subject, message body
50 % plain diff in email, subject, message body
55 requesting all changes
51 requesting all changes
56 adding changesets
52 adding changesets
57 adding manifests
53 adding manifests
58 adding file changes
54 adding file changes
59 added 1 changesets with 1 changes to 1 files
55 added 1 changesets with 2 changes to 2 files
60 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 applying ../msg.patch
57 applying ../msg.patch
62 patching file a
63 user: email patcher
58 user: email patcher
64 summary: email patch
59 summary: email patch
65 % plain diff in email, no subject, message body
60 % plain diff in email, no subject, message body
@@ -67,28 +62,25 b' requesting all changes'
67 adding changesets
62 adding changesets
68 adding manifests
63 adding manifests
69 adding file changes
64 adding file changes
70 added 1 changesets with 1 changes to 1 files
65 added 1 changesets with 2 changes to 2 files
71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 applying patch from stdin
67 applying patch from stdin
73 patching file a
74 % plain diff in email, subject, no message body
68 % plain diff in email, subject, no message body
75 requesting all changes
69 requesting all changes
76 adding changesets
70 adding changesets
77 adding manifests
71 adding manifests
78 adding file changes
72 adding file changes
79 added 1 changesets with 1 changes to 1 files
73 added 1 changesets with 2 changes to 2 files
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 applying patch from stdin
75 applying patch from stdin
82 patching file a
83 % plain diff in email, no subject, no message body, should fail
76 % plain diff in email, no subject, no message body, should fail
84 requesting all changes
77 requesting all changes
85 adding changesets
78 adding changesets
86 adding manifests
79 adding manifests
87 adding file changes
80 adding file changes
88 added 1 changesets with 1 changes to 1 files
81 added 1 changesets with 2 changes to 2 files
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 applying patch from stdin
83 applying patch from stdin
91 patching file a
92 transaction abort!
84 transaction abort!
93 rollback completed
85 rollback completed
94 % hg export in email, should use patch header
86 % hg export in email, should use patch header
@@ -96,8 +88,20 b' requesting all changes'
96 adding changesets
88 adding changesets
97 adding manifests
89 adding manifests
98 adding file changes
90 adding file changes
99 added 1 changesets with 1 changes to 1 files
91 added 1 changesets with 2 changes to 2 files
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 applying patch from stdin
93 applying patch from stdin
102 patching file a
103 summary: second change
94 summary: second change
95 % hg import in a subdirectory
96 requesting all changes
97 adding changesets
98 adding manifests
99 adding file changes
100 added 1 changesets with 2 changes to 2 files
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 applying ../../../tip.patch
103 % message should be 'subdir change'
104 summary: subdir change
105 % committer should be 'someoneelse'
106 user: someoneelse
107 % should be empty
@@ -1,6 +1,3 b''
1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 removing b
2 removing b
3 this update spans a branch affecting the following files:
3 abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
4 b
5 aborting update spanning branches!
6 (use 'hg merge' to merge across branches or 'hg update -C' to lose changes)
@@ -22,7 +22,7 b' added 1 changesets with 1 changes to 1 f'
22 (run 'hg heads' to see heads, 'hg merge' to merge)
22 (run 'hg heads' to see heads, 'hg merge' to merge)
23 merge: warning: conflicts during merge
23 merge: warning: conflicts during merge
24 resolving manifests
24 resolving manifests
25 force False allow True moddirstate True linear False
25 overwrite None branchmerge True partial False linear False
26 ancestor 055d847dd401 local 2eded9ab0a5c remote 84cf5750dd20
26 ancestor 055d847dd401 local 2eded9ab0a5c remote 84cf5750dd20
27 test.txt versions differ, resolve
27 test.txt versions differ, resolve
28 merging test.txt
28 merging test.txt
@@ -36,13 +36,19 b' kill `cat hg.pid`'
36
36
37 echo % expect success
37 echo % expect success
38 echo 'allow_push = *' >> .hg/hgrc
38 echo 'allow_push = *' >> .hg/hgrc
39 echo '[hooks]' >> .hg/hgrc
40 echo 'changegroup = echo changegroup: u=$HG_URL >> $HGTMP/urls' >> .hg/hgrc
39 hg serve -p 20059 -d --pid-file=hg.pid
41 hg serve -p 20059 -d --pid-file=hg.pid
40 cat hg.pid >> $DAEMON_PIDS
42 cat hg.pid >> $DAEMON_PIDS
41 hg --cwd ../test2 push http://localhost:20059/
43 hg --cwd ../test2 push http://localhost:20059/
42 kill `cat hg.pid`
44 kill `cat hg.pid`
43 hg rollback
45 hg rollback
44
46
47 sed 's/\(remote:http.*\):.*/\1/' $HGTMP/urls
48
45 echo % expect authorization error: all users denied
49 echo % expect authorization error: all users denied
50 echo '[web]' > .hg/hgrc
51 echo 'push_ssl = false' >> .hg/hgrc
46 echo 'deny_push = *' >> .hg/hgrc
52 echo 'deny_push = *' >> .hg/hgrc
47 hg serve -p 20059 -d --pid-file=hg.pid
53 hg serve -p 20059 -d --pid-file=hg.pid
48 cat hg.pid >> $DAEMON_PIDS
54 cat hg.pid >> $DAEMON_PIDS
@@ -20,6 +20,7 b' adding manifests'
20 adding file changes
20 adding file changes
21 added 1 changesets with 1 changes to 1 files
21 added 1 changesets with 1 changes to 1 files
22 rolling back last transaction
22 rolling back last transaction
23 changegroup: u=remote:http
23 % expect authorization error: all users denied
24 % expect authorization error: all users denied
24 pushing to http://localhost:20059/
25 pushing to http://localhost:20059/
25 searching for changes
26 searching for changes
@@ -17,6 +17,8 b' if [ ! -x dummyssh ] ; then'
17 exit -1
17 exit -1
18 fi
18 fi
19
19
20 SSH_CLIENT='127.0.0.1 1 2'
21 export SSH_CLIENT
20 echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog
22 echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog
21 $2
23 $2
22 EOF
24 EOF
@@ -29,6 +31,8 b' echo this > foo'
29 hg ci -A -m "init" -d "1000000 0" foo
31 hg ci -A -m "init" -d "1000000 0" foo
30 echo '[server]' > .hg/hgrc
32 echo '[server]' > .hg/hgrc
31 echo 'uncompressed = True' >> .hg/hgrc
33 echo 'uncompressed = True' >> .hg/hgrc
34 echo '[hooks]' >> .hg/hgrc
35 echo 'changegroup = echo changegroup in remote: u=$HG_URL >> ../dummylog' >> .hg/hgrc
32
36
33 cd ..
37 cd ..
34
38
@@ -46,6 +50,9 b' echo "# verify"'
46 cd local
50 cd local
47 hg verify
51 hg verify
48
52
53 echo '[hooks]' >> .hg/hgrc
54 echo 'changegroup = echo changegroup in local: u=$HG_URL >> ../dummylog' >> .hg/hgrc
55
49 echo "# empty default pull"
56 echo "# empty default pull"
50 hg paths
57 hg paths
51 hg pull -e ../dummyssh
58 hg pull -e ../dummyssh
@@ -83,5 +83,7 b' Got arguments 1:user@dummy 2:hg -R remot'
83 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
83 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
84 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
84 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
85 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
85 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
86 changegroup in remote: u=remote:ssh:127.0.0.1
86 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
87 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
87 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
88 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
89 changegroup in remote: u=remote:ssh:127.0.0.1
@@ -37,6 +37,14 b' http_proxy= hg clone static-http://local'
37 cd local
37 cd local
38 hg verify
38 hg verify
39 cat bar
39 cat bar
40
41 cd ../remote
42 echo baz > quux
43 hg commit -A -mtest2 -d '100000000 0'
44
45 cd ../local
46 echo '[hooks]' >> .hg/hgrc
47 echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
40 http_proxy= hg pull
48 http_proxy= hg pull
41
49
42 kill $!
50 kill $!
@@ -19,6 +19,12 b' crosschecking files in changesets and ma'
19 checking files
19 checking files
20 1 files, 1 changesets, 1 total revisions
20 1 files, 1 changesets, 1 total revisions
21 foo
21 foo
22 adding quux
23 changegroup: u=static-http://localhost:20059/remote
22 pulling from static-http://localhost:20059/remote
24 pulling from static-http://localhost:20059/remote
23 searching for changes
25 searching for changes
24 no changes found
26 adding changesets
27 adding manifests
28 adding file changes
29 added 1 changesets with 1 changes to 1 files
30 (run 'hg update' to get a working copy)
@@ -35,3 +35,8 b' hg status modified added removed deleted'
35 hg copy modified copied
35 hg copy modified copied
36 echo "hg status -C:"
36 echo "hg status -C:"
37 hg status -C
37 hg status -C
38
39 echo "hg status -t:"
40 hg status -t
41 echo "hg status -A:"
42 hg status -A
@@ -108,3 +108,50 b' A copied'
108 R removed
108 R removed
109 ! deleted
109 ! deleted
110 ? unknown
110 ? unknown
111 hg status -t:
112 hg status: option -t not recognized
113 hg status [OPTION]... [FILE]...
114
115 show changed files in the working directory
116
117 Show status of files in the repository. If names are given, only
118 files that match are shown. Files that are clean or ignored, are
119 not listed unless -c (clean), -i (ignored) or -A is given.
120
121 The codes used to show the status of files are:
122 M = modified
123 A = added
124 R = removed
125 C = clean
126 ! = deleted, but still tracked
127 ? = not tracked
128 I = ignored (not shown by default)
129 = the previous added file was copied from here
130
131 aliases: st
132
133 options:
134
135 -A --all show status of all files
136 -m --modified show only modified files
137 -a --added show only added files
138 -r --removed show only removed files
139 -d --deleted show only deleted (but tracked) files
140 -c --clean show only files without changes
141 -u --unknown show only unknown (not tracked) files
142 -i --ignored show ignored files
143 -n --no-status hide status prefix
144 -C --copies show source of copied files
145 -0 --print0 end filenames with NUL, for use with xargs
146 -I --include include names matching the given patterns
147 -X --exclude exclude names matching the given patterns
148 hg status -A:
149 A added
150 A copied
151 modified
152 R removed
153 ! deleted
154 ? unknown
155 I ignored
156 C .hgignore
157 C modified
@@ -19,6 +19,11 b' hg tag -l -d "1000000 0" "bleah1" 1'
19 cat .hgtags
19 cat .hgtags
20 cat .hg/localtags
20 cat .hg/localtags
21
21
22 hg update 0
23 hg tag -d "1000000 0" "foobar"
24 cat .hgtags
25 cat .hg/localtags
26
22 hg tag -l 'xx
27 hg tag -l 'xx
23 newline'
28 newline'
24 hg tag -l 'xx:xx'
29 hg tag -l 'xx:xx'
@@ -25,5 +25,8 b" use of 'hg tag NAME [REV]' is deprecated"
25 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
25 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
26 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
26 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
27 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
27 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
28 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
29 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
30 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
28 abort: '\n' cannot be used in a tag name
31 abort: '\n' cannot be used in a tag name
29 abort: ':' cannot be used in a tag name
32 abort: ':' cannot be used in a tag name
@@ -15,7 +15,7 b' date: Mon Jan 12 13:46:40 1970 +0'
15 summary: 1
15 summary: 1
16
16
17 resolving manifests
17 resolving manifests
18 force None allow None moddirstate True linear True
18 overwrite False branchmerge False partial False linear True
19 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
19 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
20 a versions differ, resolve
20 a versions differ, resolve
21 remote created b
21 remote created b
@@ -31,7 +31,7 b' date: Mon Jan 12 13:46:40 1970 +0'
31 summary: 2
31 summary: 2
32
32
33 resolving manifests
33 resolving manifests
34 force None allow None moddirstate True linear True
34 overwrite False branchmerge False partial False linear True
35 ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
35 ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
36 remote deleted b
36 remote deleted b
37 removing b
37 removing b
@@ -41,7 +41,7 b' user: test'
41 date: Mon Jan 12 13:46:40 1970 +0000
41 date: Mon Jan 12 13:46:40 1970 +0000
42 summary: 1
42 summary: 1
43
43
44 abort: there is nothing to merge, just use 'hg update' or look at 'hg heads'
44 abort: there is nothing to merge - use "hg update" instead
45 failed
45 failed
46 changeset: 0:33aaa84a386b
46 changeset: 0:33aaa84a386b
47 user: test
47 user: test
@@ -49,7 +49,7 b' date: Mon Jan 12 13:46:40 1970 +0'
49 summary: 1
49 summary: 1
50
50
51 resolving manifests
51 resolving manifests
52 force None allow None moddirstate True linear True
52 overwrite False branchmerge False partial False linear True
53 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
53 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
54 a versions differ, resolve
54 a versions differ, resolve
55 remote created b
55 remote created b
@@ -95,21 +95,12 b' user: test'
95 date: Mon Jan 12 13:46:40 1970 +0000
95 date: Mon Jan 12 13:46:40 1970 +0000
96 summary: 2
96 summary: 2
97
97
98 resolving manifests
98 abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
99 force None allow None moddirstate True linear False
100 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
101 a versions differ, resolve
102 b versions differ, resolve
103 this update spans a branch affecting the following files:
104 a (resolve)
105 b (resolve)
106 aborting update spanning branches!
107 (use 'hg merge' to merge across branches or 'hg update -C' to lose changes)
108 failed
99 failed
109 abort: outstanding uncommitted changes
100 abort: outstanding uncommitted changes
110 failed
101 failed
111 resolving manifests
102 resolving manifests
112 force False allow True moddirstate True linear False
103 overwrite False branchmerge True partial False linear False
113 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
104 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
114 a versions differ, resolve
105 a versions differ, resolve
115 b versions differ, resolve
106 b versions differ, resolve
@@ -40,7 +40,7 b' a'
40 side1
40 side1
41 side2
41 side2
42 resolving manifests
42 resolving manifests
43 force 1 allow None moddirstate True linear False
43 overwrite True branchmerge False partial False linear False
44 ancestor 8515d4bfda76 local 1c0f48f8ece6 remote 0594b9004bae
44 ancestor 8515d4bfda76 local 1c0f48f8ece6 remote 0594b9004bae
45 remote deleted side2, clobbering
45 remote deleted side2, clobbering
46 remote deleted side1, clobbering
46 remote deleted side1, clobbering
General Comments 0
You need to be logged in to leave comments. Login now