##// 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 1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
@@ -11,3 +11,4 b' 979c049974485125e1f9357f6bbe9c1b548a64c3'
11 11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
@@ -4,6 +4,7 b' Goffredo Baroncelli <kreijack at libero.'
4 4 Muli Ben-Yehuda <mulix at mulix.org>
5 5 Mikael Berthe <mikael at lilotux.net>
6 6 Benoit Boissinot <bboissin at gmail.com>
7 Brendan Cully <brendan at kublai.com>
7 8 Vincent Danjean <vdanjean.ml at free.fr>
8 9 Jake Edge <jake at edge2.net>
9 10 Michael Fetterman <michael.fetterman at intel.com>
@@ -2,7 +2,7 b' include hg'
2 2 recursive-include mercurial *.py
3 3 include hgweb.cgi hgwebdir.cgi
4 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 6 prune tests/*.err
7 7 include *.txt
8 8 include templates/map templates/map-*[a-z0-9]
@@ -10,8 +10,10 b' include templates/*.tmpl'
10 10 include templates/static/*
11 11 include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9]
12 12 recursive-include contrib *
13 recursive-include hgext *
13 14 include README
14 15 include CONTRIBUTORS
15 16 include COPYING
16 17 include Makefile
17 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 289 _hg_cmd_qdelete()
290 290 {
291 _hg_ext_mq_patchlist qseries
291 _hg_ext_mq_patchlist qunapplied
292 292 }
293 293
294 294 _hg_cmd_qsave()
@@ -313,6 +313,11 b' complete -o bashdefault -o default -F _h'
313 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 322 # hbisect
318 323 _hg_cmd_bisect()
@@ -28,7 +28,8 b' class convert_git:'
28 28 self.path = path
29 29
30 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 34 def catfile(self, rev, type):
34 35 if rev == "0" * 40: raise IOError()
@@ -92,7 +92,7 b' def darcs_tip(darcs_repo):'
92 92
93 93 def darcs_pull(hg_repo, darcs_repo, chash):
94 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 96 print res
97 97 new_tip = darcs_tip(darcs_repo)
98 98 if not new_tip != old_tip + 1:
@@ -110,7 +110,8 b' def hg_commit( hg_repo, text, author, da'
110 110 old_tip = hg_tip(hg_repo)
111 111 cmd("hg add -X _darcs", hg_repo)
112 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 115 os.unlink(tmpfile)
115 116 new_tip = hg_tip(hg_repo)
116 117 if not new_tip == old_tip + 1:
@@ -156,7 +157,7 b' if __name__ == "__main__":'
156 157 print "Given HG repository must not exist when no SKIP is specified."
157 158 sys.exit(-1)
158 159 if skip == None:
159 cmd("hg init '%s'" % (hg_repo))
160 cmd("hg init \"%s\"" % (hg_repo))
160 161 cmd("darcs initialize", hg_repo)
161 162 # Get the changes from the Darcs repository
162 163 change_number = 0
@@ -5,13 +5,73 b''
5 5 <meta http-equiv="Content-Style-Type" content="text/css">
6 6 <title></title>
7 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 9 p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
10 10 </style>
11 11 </head>
12 12 <body>
13 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 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 76 </body>
17 77 </html>
@@ -380,7 +380,9 b" Handle frickin' frackin' gratuitous even"
380 380 (save-excursion
381 381 (while hg-prev-buffer
382 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 386 (if (or (not path) current-prefix-arg)
385 387 (expand-file-name
386 388 (eval (list* 'read-file-name
@@ -716,7 +718,11 b' code by typing `M-x find-library mercuri'
716 718 (goto-char pos)
717 719 (end-of-line 1)
718 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 727 (defun hg-add (path)
722 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 978 (cd (hg-root path)))
973 979 (when update
974 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 983 (hg-mode-line)))))
977 984
978 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 3 " Vim plugin to assist in working with HG-controlled files.
4 4 "
5 5 " Last Change: 2006/02/22
6 " Version: 1.76
6 " Version: 1.77
7 7 " Maintainer: Mathieu Clabaut <mathieu.clabaut@gmail.com>
8 8 " License: This file is placed in the public domain.
9 9 " Credits:
@@ -13,7 +13,7 b''
13 13
14 14 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
15 15 "
16 " Section: Documentation
16 " Section: Documentation
17 17 "----------------------------
18 18 "
19 19 " Documentation should be available by ":help hgcommand" command, once the
@@ -21,7 +21,7 b''
21 21 "
22 22 " You still can read the documentation at the end of this file. Locate it by
23 23 " searching the "hgcommand-contents" string (and set ft=help to have
24 " appropriate syntaxic coloration).
24 " appropriate syntaxic coloration).
25 25
26 26 " Section: Plugin header {{{1
27 27
@@ -34,11 +34,33 b' if exists("loaded_hgcommand")'
34 34 endif
35 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 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 54 finish
40 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 64 " Section: Event group setup {{{1
43 65
44 66 augroup HGCommand
@@ -63,7 +85,7 b' unlet! s:vimDiffScratchList'
63 85 function! s:HGResolveLink(fileName)
64 86 let resolved = resolve(a:fileName)
65 87 if resolved != a:fileName
66 let resolved = s:HGResolveLink(resolved)
88 let resolved = <SID>HGResolveLink(resolved)
67 89 endif
68 90 return resolved
69 91 endfunction
@@ -74,7 +96,7 b' endfunction'
74 96
75 97 function! s:HGChangeToCurrentFileDir(fileName)
76 98 let oldCwd=getcwd()
77 let fileName=s:HGResolveLink(a:fileName)
99 let fileName=<SID>HGResolveLink(a:fileName)
78 100 let newCwd=fnamemodify(fileName, ':h')
79 101 if strlen(newCwd) > 0
80 102 execute 'cd' escape(newCwd, ' ')
@@ -82,7 +104,7 b' function! s:HGChangeToCurrentFileDir(fil'
82 104 return oldCwd
83 105 endfunction
84 106
85 " Function: s:HGGetOption(name, default) {{{2
107 " Function: <SID>HGGetOption(name, default) {{{2
86 108 " Grab a user-specified option to override the default provided. Options are
87 109 " searched in the window, buffer, then global spaces.
88 110
@@ -110,9 +132,9 b' function! s:HGEditFile(name, origBuffNR)'
110 132 "Name parameter will be pasted into expression.
111 133 let name = escape(a:name, ' *?\')
112 134
113 let editCommand = s:HGGetOption('HGCommandEdit', 'edit')
135 let editCommand = <SID>HGGetOption('HGCommandEdit', 'edit')
114 136 if editCommand != 'edit'
115 if s:HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal'
137 if <SID>HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal'
116 138 if name == ""
117 139 let editCommand = 'rightbelow new'
118 140 else
@@ -154,8 +176,8 b' function! s:HGCreateCommandBuffer(cmd, c'
154 176
155 177 let resultBufferName=''
156 178
157 if s:HGGetOption("HGCommandNameResultBuffers", 0)
158 let nameMarker = s:HGGetOption("HGCommandNameMarker", '_')
179 if <SID>HGGetOption("HGCommandNameResultBuffers", 0)
180 let nameMarker = <SID>HGGetOption("HGCommandNameMarker", '_')
159 181 if strlen(a:statusText) > 0
160 182 let bufName=a:cmdName . ' -- ' . a:statusText
161 183 else
@@ -170,7 +192,7 b' function! s:HGCreateCommandBuffer(cmd, c'
170 192 endwhile
171 193 endif
172 194
173 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd
195 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd
174 196 "echomsg "DBG :".hgCommand
175 197 let hgOut = system(hgCommand)
176 198 " HACK: diff command does not return proper error codes
@@ -192,7 +214,7 b' function! s:HGCreateCommandBuffer(cmd, c'
192 214 return -1
193 215 endif
194 216
195 if s:HGEditFile(resultBufferName, a:origBuffNR) == -1
217 if <SID>HGEditFile(resultBufferName, a:origBuffNR) == -1
196 218 return -1
197 219 endif
198 220
@@ -200,7 +222,7 b' function! s:HGCreateCommandBuffer(cmd, c'
200 222 set noswapfile
201 223 set filetype=
202 224
203 if s:HGGetOption("HGCommandDeleteOnHide", 0)
225 if <SID>HGGetOption("HGCommandDeleteOnHide", 0)
204 226 set bufhidden=delete
205 227 endif
206 228
@@ -213,8 +235,8 b' function! s:HGCreateCommandBuffer(cmd, c'
213 235 " This could be fixed by explicitly detecting whether the last line is
214 236 " within a fold, but I prefer to simply unfold the result buffer altogether.
215 237
216 if has('folding')
217 normal zR
238 if has("folding")
239 setlocal nofoldenable
218 240 endif
219 241
220 242 $d
@@ -243,7 +265,7 b' function! s:HGBufferCheck(hgBuffer)'
243 265 return origBuffer
244 266 else
245 267 " Original buffer no longer exists.
246 return -1
268 return -1
247 269 endif
248 270 else
249 271 " No original buffer
@@ -256,7 +278,7 b' endfunction'
256 278 " for the current buffer.
257 279
258 280 function! s:HGCurrentBufferCheck()
259 return s:HGBufferCheck(bufnr("%"))
281 return <SID>HGBufferCheck(bufnr("%"))
260 282 endfunction
261 283
262 284 " Function: s:HGToggleDeleteOnHide() {{{2
@@ -275,8 +297,8 b' endfunction'
275 297 " Returns: name of the new command buffer containing the command results
276 298
277 299 function! s:HGDoCommand(cmd, cmdName, statusText)
278 let hgBufferCheck=s:HGCurrentBufferCheck()
279 if hgBufferCheck == -1
300 let hgBufferCheck=<SID>HGCurrentBufferCheck()
301 if hgBufferCheck == -1
280 302 echo "Original buffer no longer exists, aborting."
281 303 return -1
282 304 endif
@@ -285,8 +307,8 b' function! s:HGDoCommand(cmd, cmdName, st'
285 307 if isdirectory(fileName)
286 308 let fileName=fileName . "/" . getline(".")
287 309 endif
288 let realFileName = fnamemodify(s:HGResolveLink(fileName), ':t')
289 let oldCwd=s:HGChangeToCurrentFileDir(fileName)
310 let realFileName = fnamemodify(<SID>HGResolveLink(fileName), ':t')
311 let oldCwd=<SID>HGChangeToCurrentFileDir(fileName)
290 312 try
291 313 " TODO
292 314 "if !filereadable('HG/Root')
@@ -294,7 +316,7 b' function! s:HGDoCommand(cmd, cmdName, st'
294 316 "endif
295 317 let fullCmd = a:cmd . ' "' . realFileName . '"'
296 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 320 return resultBuffer
299 321 catch
300 322 echoerr v:exception
@@ -314,17 +336,17 b' endfunction'
314 336 " Returns: string to be exec'd that sets the multiple return values.
315 337
316 338 function! s:HGGetStatusVars(revisionVar, branchVar, repositoryVar)
317 let hgBufferCheck=s:HGCurrentBufferCheck()
339 let hgBufferCheck=<SID>HGCurrentBufferCheck()
318 340 "echomsg "DBG : in HGGetStatusVars"
319 if hgBufferCheck == -1
341 if hgBufferCheck == -1
320 342 return ""
321 343 endif
322 344 let fileName=bufname(hgBufferCheck)
323 let fileNameWithoutLink=s:HGResolveLink(fileName)
345 let fileNameWithoutLink=<SID>HGResolveLink(fileName)
324 346 let realFileName = fnamemodify(fileNameWithoutLink, ':t')
325 let oldCwd=s:HGChangeToCurrentFileDir(realFileName)
347 let oldCwd=<SID>HGChangeToCurrentFileDir(realFileName)
326 348 try
327 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " root "
349 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " root "
328 350 let roottext=system(hgCommand)
329 351 " Suppress ending null char ! Does it work in window ?
330 352 let roottext=substitute(roottext,'^.*/\([^/\n\r]*\)\n\_.*$','\1','')
@@ -335,31 +357,31 b' function! s:HGGetStatusVars(revisionVar,'
335 357 if a:repositoryVar != ""
336 358 let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . roottext . "'"
337 359 endif
338 let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName
360 let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName
339 361 let statustext=system(hgCommand)
340 362 if(v:shell_error)
341 363 return ""
342 364 endif
343 if match(statustext, '^[?I]') >= 0
365 if match(statustext, '^[?I]') >= 0
344 366 let revision="NEW"
345 elseif match(statustext, '^[R]') >= 0
367 elseif match(statustext, '^[R]') >= 0
346 368 let revision="REMOVED"
347 elseif match(statustext, '^[D]') >= 0
369 elseif match(statustext, '^[D]') >= 0
348 370 let revision="DELETED"
349 elseif match(statustext, '^[A]') >= 0
371 elseif match(statustext, '^[A]') >= 0
350 372 let revision="ADDED"
351 373 else
352 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 376 let statustext=system(hgCommand)
355 377 if(v:shell_error)
356 return ""
378 return ""
357 379 endif
358 380 let revision=substitute(statustext, '^changeset:\s*\(\d\+\):.*\_$\_.*$', '\1', "")
359 381
360 382 if a:branchVar != "" && match(statustext, '^\_.*\_^branch:') >= 0
361 let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "")
362 let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
383 let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "")
384 let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
363 385 endif
364 386 endif
365 387 if (exists('revision'))
@@ -381,7 +403,7 b' function! s:HGSetupBuffer(...)'
381 403 return
382 404 endif
383 405
384 if !s:HGGetOption("HGCommandEnableBufferSetup", 0)
406 if !<SID>HGGetOption("HGCommandEnableBufferSetup", 0)
385 407 \ || @% == ""
386 408 \ || s:HGCommandEditFileRunning > 0
387 409 \ || exists("b:HGOrigBuffNR")
@@ -399,7 +421,7 b' function! s:HGSetupBuffer(...)'
399 421 let branch=""
400 422 let repository=""
401 423
402 exec s:HGGetStatusVars('revision', 'branch', 'repository')
424 exec <SID>HGGetStatusVars('revision', 'branch', 'repository')
403 425 "echomsg "DBG ".revision."#".branch."#".repository
404 426 if revision != ""
405 427 let b:HGRevision=revision
@@ -427,7 +449,7 b' endfunction'
427 449 function! s:HGMarkOrigBufferForSetup(hgBuffer)
428 450 checktime
429 451 if a:hgBuffer != -1
430 let origBuffer = s:HGBufferCheck(a:hgBuffer)
452 let origBuffer = <SID>HGBufferCheck(a:hgBuffer)
431 453 "This should never not work, but I'm paranoid
432 454 if origBuffer != a:hgBuffer
433 455 call setbufvar(origBuffer, "HGBufferSetup", 0)
@@ -436,7 +458,7 b' function! s:HGMarkOrigBufferForSetup(hgB'
436 458 "We are presumably in the original buffer
437 459 let b:HGBufferSetup = 0
438 460 "We do the setup now as now event will be triggered allowing it later.
439 call s:HGSetupBuffer()
461 call <SID>HGSetupBuffer()
440 462 endif
441 463 return a:hgBuffer
442 464 endfunction
@@ -478,111 +500,93 b' endfunction'
478 500 " 1 if new document installed, 0 otherwise.
479 501 " Note: Cleaned and generalized by guo-peng Wen
480 502 "'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
481
482 function! s:HGInstallDocumentation(full_name, revision)
483 " Name of the document path based on the system we use:
484 if (has("unix"))
485 " On UNIX like system, using forward slash:
486 let l:slash_char = '/'
487 let l:mkdir_cmd = ':silent !mkdir -p '
488 else
489 " On M$ system, use backslash. Also mkdir syntax is different.
490 " This should only work on W2K and up.
491 let l:slash_char = '\'
492 let l:mkdir_cmd = ':silent !mkdir '
493 endif
494
495 let l:doc_path = l:slash_char . 'doc'
496 let l:doc_home = l:slash_char . '.vim' . l:slash_char . 'doc'
503 " Helper function to make mkdir as portable as possible
504 function! s:HGFlexiMkdir(dir)
505 if exists("*mkdir") " we can use Vim's own mkdir()
506 call mkdir(a:dir)
507 elseif !exists("+shellslash")
508 call system("mkdir -p '".a:dir."'")
509 else " M$
510 let l:ssl = &shellslash
511 try
512 set shellslash
513 " no single quotes?
514 call system('mkdir "'.a:dir.'"')
515 finally
516 let &shellslash = l:ssl
517 endtry
518 endif
519 endfunction
497 520
498 " Figure out document path based on full name of this script:
499 let l:vim_plugin_path = fnamemodify(a:full_name, ':h')
500 let l:vim_doc_path = fnamemodify(a:full_name, ':h:h') . l:doc_path
501 if (!(filewritable(l:vim_doc_path) == 2))
502 echomsg "Doc path: " . l:vim_doc_path
503 execute l:mkdir_cmd . '"' . l:vim_doc_path . '"'
504 if (!(filewritable(l:vim_doc_path) == 2))
505 " Try a default configuration in user home:
506 let l:vim_doc_path = expand("~") . l:doc_home
507 if (!(filewritable(l:vim_doc_path) == 2))
508 execute l:mkdir_cmd . '"' . l:vim_doc_path . '"'
509 if (!(filewritable(l:vim_doc_path) == 2))
510 " Put a warning:
511 echomsg "Unable to open documentation directory"
512 echomsg " type :help add-local-help for more informations."
513 return 0
514 endif
515 endif
521 function! s:HGInstallDocumentation(full_name)
522 " Figure out document path based on full name of this script:
523 let l:vim_doc_path = fnamemodify(a:full_name, ":h:h") . "/doc"
524 if filewritable(l:vim_doc_path) != 2
525 echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path
526 silent! call <SID>HGFlexiMkdir(l:vim_doc_path)
527 if filewritable(l:vim_doc_path) != 2
528 " Try first item in 'runtimepath':
529 let l:vim_doc_path =
530 \ substitute(&runtimepath, '^\([^,]*\).*', '\1/doc', 'e')
531 if filewritable(l:vim_doc_path) != 2
532 echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path
533 silent! call <SID>HGFlexiMkdir(l:vim_doc_path)
534 if filewritable(l:vim_doc_path) != 2
535 " Put a warning:
536 echomsg "Unable to open documentation directory"
537 echomsg " type `:help add-local-help' for more information."
538 return 0
516 539 endif
540 endif
517 541 endif
518
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
542 endif
531 543
532 " Bail out if document file is still up to date:
533 if (filereadable(l:doc_file) &&
534 \ getftime(l:plugin_file) < getftime(l:doc_file))
535 return 0
536 endif
544 " Full name of documentation file:
545 let l:doc_file =
546 \ l:vim_doc_path . "/" . s:script_name . ".txt"
547 " Bail out if document file is still up to date:
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:
539 if (strlen(@%))
540 let l:go_back = 'b ' . bufnr("%")
541 else
542 let l:go_back = 'enew!'
543 endif
544
545 " Create a new buffer & read in the plugin file (me):
546 setl nomodeline
547 exe 'enew!'
548 exe 'r ' . l:plugin_file
549
550 setl modeline
551 let l:buf = bufnr("%")
552 setl noswapfile modifiable
553
554 norm zR
555 norm gg
553 " temporary global settings
554 let l:lz = &lazyredraw
555 let l:hls = &hlsearch
556 set lazyredraw nohlsearch
557 " Create a new buffer & read in the plugin file (me):
558 1 new
559 setlocal noswapfile modifiable nomodeline
560 if has("folding")
561 setlocal nofoldenable
562 endif
563 silent execute "read" escape(a:full_name, " ")
564 let l:doc_buf = bufnr("%")
556 565
557 " Delete from first line to a line starts with
558 " === START_DOC
559 1,/^=\{3,}\s\+START_DOC\C/ d
560
561 " Delete from a line starts with
562 " === END_DOC
563 " to the end of the documents:
564 /^=\{3,}\s\+END_DOC\C/,$ d
565
566 " Remove fold marks:
567 %s/{\{3}[1-9]/ /
566 1
567 " Delete from first line to a line starts with
568 " === START_DOC
569 silent 1,/^=\{3,}\s\+START_DOC\C/ d
570 " Delete from a line starts with
571 " === END_DOC
572 " to the end of the documents:
573 silent /^=\{3,}\s\+END_DOC\C/,$ d
568 574
569 " Add modeline for help doc: the modeline string is mangled intentionally
570 " to avoid it be recognized by VIM:
571 call append(line('$'), '')
572 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>"
575 " Add modeline for help doc: the modeline string is mangled intentionally
576 " to avoid it be recognized by VIM:
577 call append(line("$"), "")
578 call append(line("$"), " v" . "im:tw=78:ts=8:ft=help:norl:")
576 579
577 " Save the help document:
578 exe 'w! ' . l:doc_file
579 exe l:go_back
580 exe 'bw ' . l:buf
580 " Replace revision:
581 silent execute "normal :1s/#version#/" . s:script_version . "/\<CR>"
582 " Save the help document and wipe out buffer:
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:
583 exe 'helptags ' . l:vim_doc_path
584
585 return 1
587 let &hlsearch = l:hls
588 let &lazyredraw = l:lz
589 return 1
586 590 endfunction
587 591
588 592 " Section: Public functions {{{1
@@ -593,7 +597,7 b' endfunction'
593 597
594 598 function! HGGetRevision()
595 599 let revision=""
596 exec s:HGGetStatusVars('revision', '', '')
600 exec <SID>HGGetStatusVars('revision', '', '')
597 601 return revision
598 602 endfunction
599 603
@@ -612,16 +616,16 b' function! HGEnableBufferSetup()'
612 616 let g:HGCommandEnableBufferSetup=1
613 617 augroup HGCommandPlugin
614 618 au!
615 au BufEnter * call s:HGSetupBuffer()
616 au BufWritePost * call s:HGSetupBuffer()
619 au BufEnter * call <SID>HGSetupBuffer()
620 au BufWritePost * call <SID>HGSetupBuffer()
617 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 623 augroup END
620 624
621 625 " Only auto-load if the plugin is fully loaded. This gives other plugins a
622 626 " chance to run.
623 627 if g:loaded_hgcommand == 2
624 call s:HGSetupBuffer()
628 call <SID>HGSetupBuffer()
625 629 endif
626 630 endfunction
627 631
@@ -662,7 +666,7 b' endfunction'
662 666
663 667 " Function: s:HGAdd() {{{2
664 668 function! s:HGAdd()
665 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('add', 'hgadd', ''))
669 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('add', 'hgadd', ''))
666 670 endfunction
667 671
668 672 " Function: s:HGAnnotate(...) {{{2
@@ -672,7 +676,7 b' function! s:HGAnnotate(...)'
672 676 " This is a HGAnnotate buffer. Perform annotation of the version
673 677 " indicated by the current line.
674 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 680 let revision = revision - 1
677 681 endif
678 682 else
@@ -691,7 +695,7 b' function! s:HGAnnotate(...)'
691 695 return -1
692 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 699 "echomsg "DBG: ".resultBuffer
696 700 if resultBuffer != -1
697 701 set filetype=HGAnnotate
@@ -706,10 +710,10 b' function! s:HGCommit(...)'
706 710 " is used; if bang is supplied, an empty message is used; otherwise, the
707 711 " user is provided a buffer from which to edit the commit message.
708 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 714 endif
711 715
712 let hgBufferCheck=s:HGCurrentBufferCheck()
716 let hgBufferCheck=<SID>HGCurrentBufferCheck()
713 717 if hgBufferCheck == -1
714 718 echo "Original buffer no longer exists, aborting."
715 719 return -1
@@ -725,7 +729,7 b' function! s:HGCommit(...)'
725 729 let messageFileName = tempname()
726 730
727 731 let fileName=bufname(hgBufferCheck)
728 let realFilePath=s:HGResolveLink(fileName)
732 let realFilePath=<SID>HGResolveLink(fileName)
729 733 let newCwd=fnamemodify(realFilePath, ':h')
730 734 if strlen(newCwd) == 0
731 735 " Account for autochdir being in effect, which will make this blank, but
@@ -735,7 +739,7 b' function! s:HGCommit(...)'
735 739
736 740 let realFileName=fnamemodify(realFilePath, ':t')
737 741
738 if s:HGEditFile(messageFileName, hgBufferCheck) == -1
742 if <SID>HGEditFile(messageFileName, hgBufferCheck) == -1
739 743 return
740 744 endif
741 745
@@ -766,9 +770,9 b' function! s:HGCommit(...)'
766 770 silent put =\"HG: Enter Log. Lines beginning with `HG:' are removed automatically\"
767 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 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 776 silent put ='HG: or write this buffer'
773 777 endif
774 778
@@ -797,7 +801,7 b' function! s:HGDiff(...)'
797 801 let caption = ''
798 802 endif
799 803
800 let hgdiffopt=s:HGGetOption('HGCommandDiffOpt', 'w')
804 let hgdiffopt=<SID>HGGetOption('HGCommandDiffOpt', 'w')
801 805
802 806 if hgdiffopt == ""
803 807 let diffoptionstring=""
@@ -805,8 +809,8 b' function! s:HGDiff(...)'
805 809 let diffoptionstring=" -" . hgdiffopt . " "
806 810 endif
807 811
808 let resultBuffer = s:HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption)
809 if resultBuffer != -1
812 let resultBuffer = <SID>HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption)
813 if resultBuffer != -1
810 814 set filetype=diff
811 815 endif
812 816 return resultBuffer
@@ -815,7 +819,7 b' endfunction'
815 819
816 820 " Function: s:HGGotoOriginal(["!]) {{{2
817 821 function! s:HGGotoOriginal(...)
818 let origBuffNR = s:HGCurrentBufferCheck()
822 let origBuffNR = <SID>HGCurrentBufferCheck()
819 823 if origBuffNR > 0
820 824 let origWinNR = bufwinnr(origBuffNR)
821 825 if origWinNR == -1
@@ -845,11 +849,11 b' function! s:HGFinishCommit(messageFile, '
845 849 if strlen(a:targetDir) > 0
846 850 execute 'cd' escape(a:targetDir, ' ')
847 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 853 execute 'cd' escape(oldCwd, ' ')
850 854 execute 'bw' escape(a:messageFile, ' *?\')
851 855 silent execute 'call delete("' . a:messageFile . '")'
852 return s:HGMarkOrigBufferForSetup(resultBuffer)
856 return <SID>HGMarkOrigBufferForSetup(resultBuffer)
853 857 else
854 858 echoerr "Can't read message file; no commit is possible."
855 859 return -1
@@ -866,7 +870,7 b' function! s:HGLog(...)'
866 870 let caption = a:1
867 871 endif
868 872
869 let resultBuffer=s:HGDoCommand('log' . versionOption, 'hglog', caption)
873 let resultBuffer=<SID>HGDoCommand('log' . versionOption, 'hglog', caption)
870 874 if resultBuffer != ""
871 875 set filetype=rcslog
872 876 endif
@@ -875,14 +879,14 b' endfunction'
875 879
876 880 " Function: s:HGRevert() {{{2
877 881 function! s:HGRevert()
878 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('revert', 'hgrevert', ''))
882 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('revert', 'hgrevert', ''))
879 883 endfunction
880 884
881 885 " Function: s:HGReview(...) {{{2
882 886 function! s:HGReview(...)
883 887 if a:0 == 0
884 888 let versiontag=""
885 if s:HGGetOption('HGCommandInteractive', 0)
889 if <SID>HGGetOption('HGCommandInteractive', 0)
886 890 let versiontag=input('Revision: ')
887 891 endif
888 892 if versiontag == ""
@@ -896,7 +900,7 b' function! s:HGReview(...)'
896 900 let versionOption=" -r " . versiontag . " "
897 901 endif
898 902
899 let resultBuffer = s:HGDoCommand('cat' . versionOption, 'hgreview', versiontag)
903 let resultBuffer = <SID>HGDoCommand('cat' . versionOption, 'hgreview', versiontag)
900 904 if resultBuffer > 0
901 905 let &filetype=getbufvar(b:HGOrigBuffNR, '&filetype')
902 906 endif
@@ -906,18 +910,18 b' endfunction'
906 910
907 911 " Function: s:HGStatus() {{{2
908 912 function! s:HGStatus()
909 return s:HGDoCommand('status', 'hgstatus', '')
913 return <SID>HGDoCommand('status', 'hgstatus', '')
910 914 endfunction
911 915
912 916
913 917 " Function: s:HGUpdate() {{{2
914 918 function! s:HGUpdate()
915 return s:HGMarkOrigBufferForSetup(s:HGDoCommand('update', 'update', ''))
919 return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('update', 'update', ''))
916 920 endfunction
917 921
918 922 " Function: s:HGVimDiff(...) {{{2
919 923 function! s:HGVimDiff(...)
920 let originalBuffer = s:HGCurrentBufferCheck()
924 let originalBuffer = <SID>HGCurrentBufferCheck()
921 925 let s:HGCommandEditFileRunning = s:HGCommandEditFileRunning + 1
922 926 try
923 927 " If there's already a VimDiff'ed window, restore it.
@@ -925,16 +929,16 b' function! s:HGVimDiff(...)'
925 929
926 930 if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer
927 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 933 endif
930 934
931 935 " Split and diff
932 936 if(a:0 == 2)
933 937 " Reset the vimdiff system, as 2 explicit versions were provided.
934 938 if exists('s:vimDiffSourceBuffer')
935 call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
939 call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
936 940 endif
937 let resultBuffer = s:HGReview(a:1)
941 let resultBuffer = <SID>HGReview(a:1)
938 942 if resultBuffer < 0
939 943 echomsg "Can't open HG revision " . a:1
940 944 return resultBuffer
@@ -945,10 +949,10 b' function! s:HGVimDiff(...)'
945 949 let s:vimDiffScratchList = '{'. resultBuffer . '}'
946 950 " If no split method is defined, cheat, and set it to vertical.
947 951 try
948 call s:HGOverrideOption('HGCommandSplit', s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical')))
949 let resultBuffer=s:HGReview(a:2)
952 call <SID>HGOverrideOption('HGCommandSplit', <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical')))
953 let resultBuffer=<SID>HGReview(a:2)
950 954 finally
951 call s:HGOverrideOption('HGCommandSplit')
955 call <SID>HGOverrideOption('HGCommandSplit')
952 956 endtry
953 957 if resultBuffer < 0
954 958 echomsg "Can't open HG revision " . a:1
@@ -962,16 +966,16 b' function! s:HGVimDiff(...)'
962 966 " Add new buffer
963 967 try
964 968 " Force splitting behavior, otherwise why use vimdiff?
965 call s:HGOverrideOption("HGCommandEdit", "split")
966 call s:HGOverrideOption("HGCommandSplit", s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical')))
969 call <SID>HGOverrideOption("HGCommandEdit", "split")
970 call <SID>HGOverrideOption("HGCommandSplit", <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical')))
967 971 if(a:0 == 0)
968 let resultBuffer=s:HGReview()
972 let resultBuffer=<SID>HGReview()
969 973 else
970 let resultBuffer=s:HGReview(a:1)
974 let resultBuffer=<SID>HGReview(a:1)
971 975 endif
972 976 finally
973 call s:HGOverrideOption("HGCommandEdit")
974 call s:HGOverrideOption("HGCommandSplit")
977 call <SID>HGOverrideOption("HGCommandEdit")
978 call <SID>HGOverrideOption("HGCommandSplit")
975 979 endtry
976 980 if resultBuffer < 0
977 981 echomsg "Can't open current HG revision"
@@ -990,14 +994,14 b' function! s:HGVimDiff(...)'
990 994 wincmd W
991 995 execute 'buffer' originalBuffer
992 996 " Store info for later original buffer restore
993 let s:vimDiffRestoreCmd =
997 let s:vimDiffRestoreCmd =
994 998 \ "call setbufvar(".originalBuffer.", \"&diff\", ".getbufvar(originalBuffer, '&diff').")"
995 999 \ . "|call setbufvar(".originalBuffer.", \"&foldcolumn\", ".getbufvar(originalBuffer, '&foldcolumn').")"
996 1000 \ . "|call setbufvar(".originalBuffer.", \"&foldenable\", ".getbufvar(originalBuffer, '&foldenable').")"
997 1001 \ . "|call setbufvar(".originalBuffer.", \"&foldmethod\", '".getbufvar(originalBuffer, '&foldmethod')."')"
998 1002 \ . "|call setbufvar(".originalBuffer.", \"&scrollbind\", ".getbufvar(originalBuffer, '&scrollbind').")"
999 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 1005 diffthis
1002 1006 wincmd w
1003 1007 else
@@ -1027,17 +1031,17 b' endfunction'
1027 1031
1028 1032 " Section: Command definitions {{{1
1029 1033 " Section: Primary commands {{{2
1030 com! HGAdd call s:HGAdd()
1031 com! -nargs=? HGAnnotate call s:HGAnnotate(<f-args>)
1032 com! -bang -nargs=? HGCommit call s:HGCommit(<q-bang>, <q-args>)
1033 com! -nargs=* HGDiff call s:HGDiff(<f-args>)
1034 com! -bang HGGotoOriginal call s:HGGotoOriginal(<q-bang>)
1035 com! -nargs=? HGLog call s:HGLog(<f-args>)
1036 com! HGRevert call s:HGRevert()
1037 com! -nargs=? HGReview call s:HGReview(<f-args>)
1038 com! HGStatus call s:HGStatus()
1039 com! HGUpdate call s:HGUpdate()
1040 com! -nargs=* HGVimDiff call s:HGVimDiff(<f-args>)
1034 com! HGAdd call <SID>HGAdd()
1035 com! -nargs=? HGAnnotate call <SID>HGAnnotate(<f-args>)
1036 com! -bang -nargs=? HGCommit call <SID>HGCommit(<q-bang>, <q-args>)
1037 com! -nargs=* HGDiff call <SID>HGDiff(<f-args>)
1038 com! -bang HGGotoOriginal call <SID>HGGotoOriginal(<q-bang>)
1039 com! -nargs=? HGLog call <SID>HGLog(<f-args>)
1040 com! HGRevert call <SID>HGRevert()
1041 com! -nargs=? HGReview call <SID>HGReview(<f-args>)
1042 com! HGStatus call <SID>HGStatus()
1043 com! HGUpdate call <SID>HGUpdate()
1044 com! -nargs=* HGVimDiff call <SID>HGVimDiff(<f-args>)
1041 1045
1042 1046 " Section: HG buffer management commands {{{2
1043 1047 com! HGDisableBufferSetup call HGDisableBufferSetup()
@@ -1173,7 +1177,7 b' endfunction'
1173 1177
1174 1178 augroup HGVimDiffRestore
1175 1179 au!
1176 au BufUnload * call s:HGVimDiffRestore(expand("<abuf>"))
1180 au BufUnload * call <SID>HGVimDiffRestore(expand("<abuf>"))
1177 1181 augroup END
1178 1182
1179 1183 " Section: Optional activation of buffer management {{{1
@@ -1183,20 +1187,24 b" if s:HGGetOption('HGCommandEnableBufferS"
1183 1187 endif
1184 1188
1185 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 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 1203 let loaded_hgcommand=2
1199 1204 silent do HGCommand User HGPluginFinish
1205
1206 let &cpo = s:save_cpo
1207 unlet s:save_cpo
1200 1208 " vim:se expandtab sts=2 sw=2:
1201 1209 finish
1202 1210
@@ -1228,16 +1236,16 b' 1. Contents\t\t\t\t\t\t *hgcommand-contents*'
1228 1236 ==============================================================================
1229 1237 2. HGCommand Installation *hgcommand-install*
1230 1238
1231 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
1239 In order to install the plugin, place the hgcommand.vim file into a plugin'
1240 directory in your runtime path (please see |add-global-plugin| and
1233 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 1244 specifying event handlers. Please see |hgcommand-customize| for more
1237 1245 details.
1238 1246
1239 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 1249 loaded for the first time.
1242 1250
1243 1251 ==============================================================================
@@ -1245,32 +1253,32 b' 2. HGCommand Installation\t\t\t\t *hgcomma'
1245 1253 3. HGCommand Intro *hgcommand*
1246 1254 *hgcommand-intro*
1247 1255
1248 The HGCommand plugin provides global ex commands for manipulating
1249 HG-controlled source files. In general, each command operates on the
1250 current buffer and accomplishes a separate hg function, such as update,
1256 The HGCommand plugin provides global ex commands for manipulating
1257 HG-controlled source files. In general, each command operates on the
1258 current buffer and accomplishes a separate hg function, such as update,
1251 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
1253 scratch buffer. Several buffer variables are defined for those scratch
1260 available commands). The results of each operation are displayed in a
1261 scratch buffer. Several buffer variables are defined for those scratch
1254 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 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
1260 as though it was invoked on the original file and splits the screen so that
1267 For convenience, any HGCommand invoked on a HGCommand scratch buffer acts
1268 as though it was invoked on the original file and splits the screen so that
1261 1269 the output appears in a new window.
1262 1270
1263 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
1271 Many of the commands accept revisions as arguments. By default, most
1272 operate on the most recent revision on the current branch if no revision is
1265 1273 specified (though see |HGCommandInteractive| to prompt instead).
1266 1274
1267 Each HGCommand is mapped to a key sequence starting with the <Leader>
1268 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
1270 fashion for plugin mappings. For examples, please see
1275 Each HGCommand is mapped to a key sequence starting with the <Leader>
1276 keystroke. The default mappings may be overridden by supplying different
1277 mappings before the plugin is loaded, such as in the vimrc, in the standard
1278 fashion for plugin mappings. For examples, please see
1271 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 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 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 1306 not commit the newly-added file.
1299 1307
1300 1308 :HGAnnotate *:HGAnnotate*
1301 1309
1302 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
1304 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
1310 This command performs "hg annotate" on the current file. If an argument is
1311 given, the argument is used as a revision number to display. If not given
1312 an argument, it uses the most recent version of the file on the current
1313 branch. Additionally, if the current buffer is a HGAnnotate buffer
1306 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
1309 version previous to the one on the current line is used instead. This
1316 If the |HGCommandAnnotateParent| variable is set to a non-zero value, the
1317 version previous to the one on the current line is used instead. This
1310 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 1321 take advantage of the bundled syntax file.
1314 1322
1315 1323
1316 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 1327 the log message.
1320 1328
1321 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
1324 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
1326 that log message. The commit can be abandoned if the log message buffer is
1331 If called with no arguments, this is a two-step command. The first step
1332 opens a buffer to accept a log message. When that buffer is written, it is
1333 automatically closed and the file is committed using the information from
1334 that log message. The commit can be abandoned if the log message buffer is
1327 1335 deleted or wiped before being written.
1328 1336
1329 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.
1331 This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to
1337 Alternatively, the mapping that is used to invoke :HGCommit (by default
1338 <Leader>hgc) can be used in the log message buffer to immediately commit.
1339 This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to
1332 1340 disable the normal commit-on-write behavior.
1333 1341
1334 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 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 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 1351 the current file.
1344 1352
1345 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
1353 This command uses the 'HGCommandDiffOpt' variable to specify diff options.
1354 If that variable does not exist, then 'wbBc' is assumed. If you wish to
1347 1355 have no options, then set it to the empty string.
1348 1356
1349 1357
1350 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 1361 current buffer is a HG command output buffer.
1354 1362
1355 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 1366 output buffers for the source buffer.
1359 1367
1360 1368 :HGLog *:HGLog*
1361 1369
1362 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 1373 "hg log".
1366 1374
1367 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 1378 in order to wipe out any undesired changes.
1371
1379
1372 1380 :HGReview *:HGReview*
1373 1381
1374 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
1382 Retrieves a particular version of the current file. If no argument is
1383 given, the most recent version of the file on the current branch is
1376 1384 retrieved. Otherwise, the specified version is retrieved.
1377 1385
1378 1386 :HGStatus *:HGStatus*
@@ -1381,37 +1389,37 b' 4.1 HGCommand commands\t\t\t\t\t *hgcommand-'
1381 1389
1382 1390 :HGUpdate *:HGUpdate*
1383 1391
1384 Performs "hg update" on the current file. This intentionally does not
1385 automatically reload the current buffer, though vim should prompt the user
1392 Performs "hg update" on the current file. This intentionally does not
1393 automatically reload the current buffer, though vim should prompt the user
1386 1394 to do so if the underlying file is altered by this command.
1387 1395
1388 1396 :HGVimDiff *:HGVimDiff*
1389 1397
1390 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
1392 specified revision. If no revision is specified, the most recent version
1398 With no arguments, this prompts the user for a revision and then uses
1399 vimdiff to display the differences between the current file and the
1400 specified revision. If no revision is specified, the most recent version
1393 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
1396 two arguments, the differences between the two revisions is displayed using
1403 With one argument, that argument is used as the revision as above. With
1404 two arguments, the differences between the two revisions is displayed using
1397 1405 vimdiff.
1398 1406
1399 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
1407 With either zero or one argument, the original buffer is used to perform
1408 the vimdiff. When the other buffer is closed, the original buffer will be
1401 1409 returned to normal mode.
1402 1410
1403 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.
1411 Once vimdiff mode is started using the above methods, additional vimdiff
1412 buffers may be added by passing a single version argument to the command.
1405 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
1408 versions. Additionally, invoking the command on a different file will
1415 Using the 2-argument form of the command resets the vimdiff to only those 2
1416 versions. Additionally, invoking the command on a different file will
1409 1417 close the previous vimdiff buffers.
1410 1418
1411 1419
1412 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 1423 the default (no-argument) form of each command.
1416 1424
1417 1425 <Leader>hga HGAdd
@@ -1428,20 +1436,20 b' 4.2 Mappings\t\t\t\t\t\t *hgcommand-mappings*'
1428 1436
1429 1437 *hgcommand-mappings-override*
1430 1438
1431 The default mappings can be overriden by user-provided instead by mapping
1432 to <Plug>CommandName. This is especially useful when these mappings
1433 collide with other existing mappings (vim will warn of this during plugin
1439 The default mappings can be overriden by user-provided instead by mapping
1440 to <Plug>CommandName. This is especially useful when these mappings
1441 collide with other existing mappings (vim will warn of this during plugin
1434 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 1445 '\add', add the following to the vimrc: >
1438 1446
1439 1447 nmap \add <Plug>HGAdd
1440 1448 <
1441 1449 4.3 Automatic buffer variables *hgcommand-buffer-variables*
1442 1450
1443 Several buffer variables are defined in each HGCommand result buffer.
1444 These may be useful for additional customization in callbacks defined in
1451 Several buffer variables are defined in each HGCommand result buffer.
1452 These may be useful for additional customization in callbacks defined in
1445 1453 the event handlers (please see |hgcommand-events|).
1446 1454
1447 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 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 1464 buffer.
1457 1465 ==============================================================================
1458 1466
1459 1467 5. Configuration and customization *hgcommand-customize*
1460 1468 *hgcommand-config*
1461 1469
1462 The HGCommand plugin can be configured in two ways: by setting
1463 configuration variables (see |hgcommand-options|) or by defining HGCommand
1464 event handlers (see |hgcommand-events|). Additionally, the HGCommand
1465 plugin provides several option for naming the HG result buffers (see
1466 |hgcommand-naming|) and supported a customized status line (see
1470 The HGCommand plugin can be configured in two ways: by setting
1471 configuration variables (see |hgcommand-options|) or by defining HGCommand
1472 event handlers (see |hgcommand-events|). Additionally, the HGCommand
1473 plugin provides several option for naming the HG result buffers (see
1474 |hgcommand-naming|) and supported a customized status line (see
1467 1475 |hgcommand-statusline| and |hgcommand-buffer-management|).
1468 1476
1469 1477 5.1 HGCommand configuration variables *hgcommand-options*
1470 1478
1471 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
1479 Several variables affect the plugin's behavior. These variables are
1480 checked at time of execution, and may be defined at the window, buffer, or
1473 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 1499 HGCommandAnnotateParent *HGCommandAnnotateParent*
1492 1500
1493 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
1495 previous to that displayed on the current line. If not set, it defaults to
1501 This variable, if set to a non-zero value, causes the zero-argument form of
1502 HGAnnotate when invoked on a HGAnnotate buffer to go to the version
1503 previous to that displayed on the current line. If not set, it defaults to
1496 1504 0.
1497 1505
1498 1506 HGCommandCommitOnWrite *HGCommandCommitOnWrite*
1499 1507
1500 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
1502 set to zero, only the HGCommit mapping will cause the pending commit to
1508 This variable, if set to a non-zero value, causes the pending hg commit to
1509 take place immediately as soon as the log message buffer is written. If
1510 set to zero, only the HGCommit mapping will cause the pending commit to
1503 1511 occur. If not set, it defaults to 1.
1504 1512
1505 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 1516 set, it defaults to "hg".
1509 1517
1510 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 1521 buffers to automatically delete themselves when hidden.
1514 1522
1515 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 1526 HG. If not set, it defaults to 'w'.
1519 1527
1520 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 1531 created with |:HGVimDiff|.
1524 1532
1525 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 1536 split ('split'). If not set, it defaults to 'edit'.
1529 1537
1530 1538 HGCommandEnableBufferSetup *HGCommandEnableBufferSetup*
1531 1539
1532 This variable, if set to a non-zero value, activates HG buffer management
1533 mode see (|hgcommand-buffer-management|). This mode means that three
1534 buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if
1535 the file is HG-controlled. This is useful for displaying version
1540 This variable, if set to a non-zero value, activates HG buffer management
1541 mode see (|hgcommand-buffer-management|). This mode means that three
1542 buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if
1543 the file is HG-controlled. This is useful for displaying version
1536 1544 information in the status bar.
1537 1545
1538 1546 HGCommandInteractive *HGCommandInteractive*
1539 1547
1540 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
1548 This variable, if set to a non-zero value, causes appropriate commands (for
1549 the moment, only |:HGReview|) to query the user for a revision to use
1542 1550 instead of the current revision if none is specified.
1543 1551
1544 1552 HGCommandNameMarker *HGCommandNameMarker*
1545 1553
1546 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
1548 has no effect unless |HGCommandNameResultBuffers| is set to a true value.
1549 If not set, it defaults to '_'.
1554 This variable, if set, configures the special attention-getting characters
1555 that appear on either side of the hg buffer type in the buffer name. This
1556 has no effect unless |HGCommandNameResultBuffers| is set to a true value.
1557 If not set, it defaults to '_'.
1550 1558
1551 1559 HGCommandNameResultBuffers *HGCommandNameResultBuffers*
1552 1560
1553 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
1561 This variable, if set to a true value, causes the hg result buffers to be
1562 named in the old way ('<source file name> _<hg command>_'). If not set or
1555 1563 set to a false value, the result buffer is nameless.
1556 1564
1557 1565 HGCommandSplit *HGCommandSplit*
1558 1566
1559 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
1561 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
1563 another. If set to 'vertical', the resulting windows will be side-by-side.
1567 This variable controls the orientation of the various window splits that
1568 may occur (such as with HGVimDiff, when using a HG command on a HG command
1569 buffer, or when the |HGCommandEdit| variable is set to 'split'. If set to
1570 'horizontal', the resulting windows will be on stacked on top of one
1571 another. If set to 'vertical', the resulting windows will be side-by-side.
1564 1572 If not set, it defaults to 'horizontal' for all but HGVimDiff windows.
1565 1573
1566 1574 5.2 HGCommand events *hgcommand-events*
1567 1575
1568 For additional customization, HGCommand can trigger user-defined events.
1569 Event handlers are provided by defining User event autocommands (see
1570 |autocommand|, |User|) in the HGCommand group with patterns matching the
1576 For additional customization, HGCommand can trigger user-defined events.
1577 Event handlers are provided by defining User event autocommands (see
1578 |autocommand|, |User|) in the HGCommand group with patterns matching the
1571 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 1582 mapping to quit a HGCommand scratch buffer: >
1575 1583
1576 1584 augroup HGCommand
@@ -1582,10 +1590,10 b' 5.2 HGCommand events\t\t\t\t *hgc'
1582 1590 The following hooks are available:
1583 1591
1584 1592 HGBufferCreated This event is fired just after a hg command result
1585 buffer is created and filled with the result of a hg
1586 command. It is executed within the context of the HG
1587 command buffer. The HGCommand buffer variables may be
1588 useful for handlers of this event (please see
1593 buffer is created and filled with the result of a hg
1594 command. It is executed within the context of the HG
1595 command buffer. The HGCommand buffer variables may be
1596 useful for handlers of this event (please see
1589 1597 |hgcommand-buffer-variables|).
1590 1598
1591 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 1606 loads.
1599 1607
1600 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 1610 window placement and focus.
1603 1611
1604 1612 5.3 HGCommand buffer naming *hgcommand-naming*
1605 1613
1606 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
1608 used to customize the statusline option so that the user may fully control
1614 By default, the buffers containing the result of HG commands are nameless
1615 scratch buffers. It is intended that buffer variables of those buffers be
1616 used to customize the statusline option so that the user may fully control
1609 1617 the display of result buffers.
1610 1618
1611 If the old-style naming is desired, please enable the
1612 |HGCommandNameResultBuffers| variable. Then, each result buffer will
1613 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
1619 If the old-style naming is desired, please enable the
1620 |HGCommandNameResultBuffers| variable. Then, each result buffer will
1621 receive a unique name that includes the source file name, the HG command,
1622 and any extra data (such as revision numbers) that were part of the
1615 1623 command.
1616 1624
1617 1625 5.4 HGCommand status line support *hgcommand-statusline*
1618 1626
1619 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
1621 the |'statusline'| option is provided by the plugin, HGGetStatusLine(). In
1622 order to use that function in the status line, do something like the
1627 It is intended that the user will customize the |'statusline'| option to
1628 include HG result buffer attributes. A sample function that may be used in
1629 the |'statusline'| option is provided by the plugin, HGGetStatusLine(). In
1630 order to use that function in the status line, do something like the
1623 1631 following: >
1624 1632
1625 1633 set statusline=%<%f\ %{HGGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P
1626 1634 <
1627 1635 of which %{HGGetStatusLine()} is the relevant portion.
1628 1636
1629 The sample HGGetStatusLine() function handles both HG result buffers and
1630 HG-managed files if HGCommand buffer management is enabled (please see
1637 The sample HGGetStatusLine() function handles both HG result buffers and
1638 HG-managed files if HGCommand buffer management is enabled (please see
1631 1639 |hgcommand-buffer-management|).
1632 1640
1633 1641 5.5 HGCommand buffer management *hgcommand-buffer-management*
1634 1642
1635 The HGCommand plugin can operate in buffer management mode, which means
1636 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
1638 status' will be invoked at each entry into a buffer (during the |BufEnter|
1643 The HGCommand plugin can operate in buffer management mode, which means
1644 that it attempts to set two buffer variables ('HGRevision' and 'HGBranch')
1645 upon entry into a buffer. This is rather slow because it means that 'hg
1646 status' will be invoked at each entry into a buffer (during the |BufEnter|
1639 1647 autocommand).
1640 1648
1641 This mode is enablmed by default. In order to disable it, set the
1642 |HGCommandEnableBufferSetup| variable to a false (zero) value. Enabling
1643 this mode simply provides the buffer variables mentioned above. The user
1644 must explicitly include those in the |'statusline'| option if they are to
1649 This mode is enabled by default. In order to disable it, set the
1650 |HGCommandEnableBufferSetup| variable to a false (zero) value. Enabling
1651 this mode simply provides the buffer variables mentioned above. The user
1652 must explicitly include those in the |'statusline'| option if they are to
1645 1653 appear in the status line (but see |hgcommand-statusline| for a simple way
1646 1654 to do that).
1647 1655
@@ -1655,10 +1663,10 b' 9.1 Split window annotation, by Michael '
1655 1663 \:set nowrap<CR>
1656 1664 <
1657 1665
1658 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
1660 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
1666 This splits the buffer vertically, puts an annotation on the left (minus
1667 the header) with the width set to 40. An editable/normal copy is placed on
1668 the right. The two versions are scroll locked so they move as one. and
1669 wrapping is turned off so that the lines line up correctly. The advantages
1662 1670 are...
1663 1671
1664 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 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
1675 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
1682 HGVimDiff, when using the original (real) source buffer as one of the diff
1683 buffers, uses some hacks to try to restore the state of the original buffer
1684 when the scratch buffer containing the other version is destroyed. There
1677 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 1695 " v im:tw=78:ts=8:ft=help:norl:
1688 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 14 </head>
15 15
16 16 <body>
17 <h1>Mercurial version 0.9 for Windows</h1>
17 <h1>Mercurial version 0.9.1 for Windows</h1>
18 18
19 19 <p>Welcome to Mercurial for Windows!</p>
20 20
@@ -4,7 +4,7 b''
4 4 [Setup]
5 5 AppCopyright=Copyright 2005, 2006 Matt Mackall and others
6 6 AppName=Mercurial
7 AppVerName=Mercurial version 0.9
7 AppVerName=Mercurial version 0.9.1
8 8 InfoAfterFile=contrib/win32/postinstall.txt
9 9 LicenseFile=COPYING
10 10 ShowLanguageDialog=yes
@@ -14,10 +14,10 b' AppSupportURL=http://www.selenic.com/mer'
14 14 AppUpdatesURL=http://www.selenic.com/mercurial
15 15 AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}
16 16 AppContact=mercurial@selenic.com
17 OutputBaseFilename=Mercurial-0.9
17 OutputBaseFilename=Mercurial-0.9.1
18 18 DefaultDirName={sd}\Mercurial
19 19 SourceDir=C:\hg\hg-release
20 VersionInfoVersion=0.9
20 VersionInfoVersion=0.9.1
21 21 VersionInfoDescription=Mercurial distributed SCM
22 22 VersionInfoCopyright=Copyright 2005, 2006 Matt Mackall and others
23 23 VersionInfoCompany=Matt Mackall and others
@@ -7,6 +7,62 b' file that comes with this package.'
7 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 66 2006-05-10 v0.9
11 67
12 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 217 COPYING
218 218 -------
219 Copyright \(C) 2005 Matt Mackall.
219 Copyright \(C) 2005, 2006 Matt Mackall.
220 220 Free use of this software is granted under the terms of the GNU General
221 221 Public License (GPL).
@@ -30,6 +30,6 b' hg(1) - the command line interface to Me'
30 30
31 31 COPYING
32 32 -------
33 Copyright \(C) 2005 Matt Mackall.
33 Copyright \(C) 2005, 2006 Matt Mackall.
34 34 Free use of this software is granted under the terms of the GNU General
35 35 Public License (GPL).
@@ -138,9 +138,17 b' email::'
138 138 from;;
139 139 Optional. Email address to use in "From" header and SMTP envelope
140 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 149 method;;
142 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 152 configuration). Otherwise, use as name of program to run that
145 153 acts like sendmail (takes "-f" option for sender, list of
146 154 recipients on command line, message on stdin). Normally, setting
@@ -194,7 +202,8 b' hooks::'
194 202
195 203 changegroup;;
196 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 207 commit;;
199 208 Run after a changeset has been created in the local repository.
200 209 ID of the newly created changeset is in $HG_NODE. Parent
@@ -202,7 +211,7 b' hooks::'
202 211 incoming;;
203 212 Run after a changeset has been pulled, pushed, or unbundled into
204 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 215 outgoing;;
207 216 Run after sending changes from local repository to another. ID of
208 217 first changeset sent is in $HG_NODE. Source of operation is in
@@ -210,7 +219,8 b' hooks::'
210 219 prechangegroup;;
211 220 Run before a changegroup is added via push, pull or unbundle.
212 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 224 precommit;;
215 225 Run before starting a local commit. Exit status 0 allows the
216 226 commit to proceed. Non-zero status will cause the commit to fail.
@@ -236,7 +246,8 b' hooks::'
236 246 before accepting them. Passed the ID of the first new changeset
237 247 in $HG_NODE. Exit status 0 allows the transaction to commit.
238 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 251 pretxncommit;;
241 252 Run after a changeset has been created but the transaction not yet
242 253 committed. Changeset is visible to hook program. This lets you
@@ -295,7 +306,7 b' http_proxy::'
295 306 smtp::
296 307 Configuration for extensions that need to send email messages.
297 308 host;;
298 Optional. Host name of mail server. Default: "mail".
309 Host name of mail server, e.g. "mail.example.com".
299 310 port;;
300 311 Optional. Port to connect to on mail server. Default: 25.
301 312 tls;;
@@ -440,6 +451,9 b' web::'
440 451 push_ssl;;
441 452 Whether to require that inbound pushes be transported over SSL to
442 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 457 style;;
444 458 Which template map style to use.
445 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 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 36 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
37 37 認められます。
@@ -5,34 +5,49 b''
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 # allow to use external programs to compare revisions, or revision
9 # with working dir. program is called with two arguments: paths to
10 # directories containing snapshots of files to compare.
8 # The `extdiff' Mercurial extension allows you to use external programs
9 # to compare revisions, or revision with working dir. The external diff
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 16 # [extensions]
15 17 # hgext.extdiff =
16 18 #
17 # also allows to configure new diff commands, so you do not need to
18 # type "hg extdiff -p kdiff3" always.
19 # The `extdiff' extension also allows to configure new diff commands, so
20 # you do not need to type "hg extdiff -p kdiff3" always.
19 21 #
20 22 # [extdiff]
23 # # add new command that runs GNU diff(1) in 'context diff' mode
24 # cmd.cdiff = gdiff
25 # opts.cdiff = -Nprc5
21 26 # # add new command called vdiff, runs kdiff3
22 27 # cmd.vdiff = kdiff3
23 28 # # add new command called meld, runs meld (no need to name twice)
24 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
27 # "hg diff" command. extdiff makes snapshots of only needed files, so
28 # compare program will be fast.
34 # Each custom diff commands can have two parts: a `cmd' and an `opts'
35 # part. The cmd.xxx option defines the name of an executable program
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 45 from mercurial.demandload import demandload
31 46 from mercurial.i18n import gettext as _
32 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 51 def snapshot_node(files, node):
37 52 '''snapshot files as of some revision'''
38 53 changes = repo.changelog.read(node)
@@ -76,9 +91,9 b' def dodiff(ui, repo, diffcmd, pats, opts'
76 91 return dirname
77 92
78 93 node1, node2 = commands.revpair(ui, repo, opts['rev'])
79 files, matchfn, anypats = commands.matchpats(repo, pats, opts)
80 modified, added, removed, deleted, unknown = repo.changes(
81 node1, node2, files, match=matchfn)
94 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
95 modified, added, removed, deleted, unknown = repo.status(
96 node1, node2, files, match=matchfn)[:5]
82 97 if not (modified or added or removed):
83 98 return 0
84 99
@@ -89,9 +104,12 b' def dodiff(ui, repo, diffcmd, pats, opts'
89 104 dir2 = snapshot_node(modified + added, node2)
90 105 else:
91 106 dir2 = snapshot_wdir(modified + added)
92 util.system('%s %s "%s" "%s"' %
93 (diffcmd, ' '.join(opts['option']), dir1, dir2),
94 cwd=tmproot)
107 cmdline = ('%s %s %s %s' %
108 (util.shellquote(diffcmd),
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 113 return 1
96 114 finally:
97 115 ui.note(_('cleaning up temp directory\n'))
@@ -101,7 +119,9 b' def extdiff(ui, repo, *pats, **opts):'
101 119 '''use external program to diff repository (or selected files)
102 120
103 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 125 To select a different program, use the -p option. The program
106 126 will be passed the names of two directories to compare. To pass
107 127 additional options to the program, use the -o option. These will
@@ -112,7 +132,8 b' def extdiff(ui, repo, *pats, **opts):'
112 132 specified then that revision is compared to the working
113 133 directory, and, when no revisions are specified, the
114 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 138 cmdtable = {
118 139 "extdiff":
@@ -130,20 +151,24 b' def uisetup(ui):'
130 151 if not cmd.startswith('cmd.'): continue
131 152 cmd = cmd[4:]
132 153 if not path: path = cmd
154 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
155 diffopts = diffopts and [diffopts] or []
133 156 def save(cmd, path):
134 157 '''use closure to save diff command to use'''
135 158 def mydiff(ui, repo, *pats, **opts):
136 return dodiff(ui, repo, path, pats, opts)
137 mydiff.__doc__ = '''use %s to diff repository (or selected files)
159 return dodiff(ui, repo, path, diffopts, pats, opts)
160 mydiff.__doc__ = '''use %(path)r to diff repository (or selected files)
138 161
139 162 Show differences between revisions for the specified
140 files, using the %s program.
163 files, using the %(path)r program.
141 164
142 165 When two revision arguments are given, then changes are
143 166 shown between those revisions. If only one revision is
144 167 specified then that revision is compared to the working
145 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 172 return mydiff
148 173 cmdtable[cmd] = (save(cmd, path),
149 174 cmdtable['extdiff'][1][1:],
@@ -221,7 +221,7 b' def sign(ui, repo, *revs, **opts):'
221 221 repo.opener("localsigs", "ab").write(sigmessage)
222 222 return
223 223
224 for x in repo.changes():
224 for x in repo.status()[:5]:
225 225 if ".hgsigs" in x and not opts["force"]:
226 226 raise util.Abort(_("working copy of .hgsigs is changed "
227 227 "(please commit .hgsigs manually "
@@ -23,10 +23,10 b' def lookup_rev(ui, repo, rev=None):'
23 23 return parents.pop()
24 24
25 25 def check_clean(ui, repo):
26 modified, added, removed, deleted, unknown = repo.changes()
27 if modified or added or removed:
28 ui.warn("Repository is not clean, please commit or revert\n")
29 sys.exit(1)
26 modified, added, removed, deleted, unknown = repo.status()[:5]
27 if modified or added or removed:
28 ui.warn("Repository is not clean, please commit or revert\n")
29 sys.exit(1)
30 30
31 31 class bisect(object):
32 32 """dichotomic search in the DAG of changesets"""
@@ -50,7 +50,7 b' class bisect(object):'
50 50 if r:
51 51 self.badrev = hg.bin(r.pop(0))
52 52
53 def __del__(self):
53 def write(self):
54 54 if not os.path.isdir(self.path):
55 55 return
56 56 f = self.opener(self.good_path, "w")
@@ -197,7 +197,7 b' class bisect(object):'
197 197 check_clean(self.ui, self.repo)
198 198 rev = self.next()
199 199 if rev is not None:
200 return self.repo.update(rev, force=True)
200 return hg.clean(self.repo, rev)
201 201
202 202 def good(self, rev):
203 203 self.goodrevs.append(rev)
@@ -288,7 +288,10 b' for subcommands see "hg bisect help\\"'
288 288 if len(args) > bisectcmdtable[cmd][1]:
289 289 ui.warn(_("bisect: Too many arguments\n"))
290 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 296 cmdtable = {
294 297 "bisect": (bisect_run, [], _("hg bisect [help|init|reset|next|good|bad]")),
@@ -1,12 +1,13 b''
1 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 import time, sys, signal, os
9 from mercurial import hg, mdiff, fancyopts, commands, ui, util
8 from mercurial.demandload import *
9 demandload(globals(), 'time sys signal os')
10 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util')
10 11
11 12 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
12 13 changes=None, text=False):
@@ -14,7 +15,7 b' def dodiff(fp, ui, repo, node1, node2, f'
14 15 return time.asctime(time.gmtime(c[2][0]))
15 16
16 17 if not changes:
17 changes = repo.changes(node1, node2, files, match=match)
18 changes = repo.status(node1, node2, files, match=match)[:5]
18 19 modified, added, removed, deleted, unknown = changes
19 20 if files:
20 21 modified, added, removed = map(lambda x: filterfiles(files, x),
@@ -67,12 +68,12 b' def difftree(ui, repo, node1=None, node2'
67 68 if node2:
68 69 change = repo.changelog.read(node2)
69 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 72 def read(f): return repo.file(f).read(mmap2[f])
72 73 date2 = date(change)
73 74 else:
74 75 date2 = time.asctime()
75 modified, added, removed, deleted, unknown = repo.changes(node1)
76 modified, added, removed, deleted, unknown = repo.status(node1)[:5]
76 77 if not node1:
77 78 node1 = repo.dirstate.parents()[0]
78 79 def read(f): return file(os.path.join(repo.root, f)).read()
@@ -334,6 +335,3 b' cmdtable = {'
334 335 ('n', 'max-count', 0, 'max-count')],
335 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 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 5 # This software may be used and distributed according to the terms
6 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 32 from mercurial.demandload import *
33 from mercurial.i18n import gettext as _
33 34 demandload(globals(), "os sys re struct traceback errno bz2")
34 from mercurial.i18n import gettext as _
35 from mercurial import ui, hg, revlog, commands, util
35 demandload(globals(), "mercurial:cmdutil,commands,hg,patch,revlog,ui,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 53 class queue:
43 54 def __init__(self, ui, path, patchdir=None):
44 55 self.basepath = path
45 if patchdir:
46 self.path = patchdir
47 else:
48 self.path = os.path.join(path, "patches")
56 self.path = patchdir or os.path.join(path, "patches")
49 57 self.opener = util.opener(self.path)
50 58 self.ui = ui
51 59 self.applied = []
@@ -54,13 +62,26 b' class queue:'
54 62 self.series_dirty = 0
55 63 self.series_path = "series"
56 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 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)):
63 self.applied = self.opener(self.status_path).read().splitlines()
78 def diffopts(self):
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 86 def find_series(self, patch):
66 87 pre = re.compile("(\s*)([^#]+)")
@@ -75,34 +96,132 b' class queue:'
75 96 index += 1
76 97 return None
77 98
78 def read_series(self, list):
79 def matcher(list):
80 pre = re.compile("(\s*)([^#]+)")
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
99 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
100
101 def parse_series(self):
88 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 216 def save_dirty(self):
92 if self.applied_dirty:
93 if len(self.applied) > 0:
94 nl = "\n"
95 else:
96 nl = ""
97 f = self.opener(self.status_path, "w")
98 f.write("\n".join(self.applied) + nl)
99 if self.series_dirty:
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)
217 def write_list(items, path):
218 fp = self.opener(path, 'w')
219 for i in items:
220 print >> fp, i
221 fp.close()
222 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
223 if self.series_dirty: write_list(self.full_series, self.series_path)
224 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
106 225
107 226 def readheaders(self, patch):
108 227 def eatdiff(lines):
@@ -122,7 +241,7 b' class queue:'
122 241 else:
123 242 break
124 243
125 pf = os.path.join(self.path, patch)
244 pf = self.join(patch)
126 245 message = []
127 246 comments = []
128 247 user = None
@@ -133,6 +252,9 b' class queue:'
133 252
134 253 for line in file(pf):
135 254 line = line.rstrip()
255 if line.startswith('diff --git'):
256 diffstart = 2
257 break
136 258 if diffstart:
137 259 if line.startswith('+++ '):
138 260 diffstart = 2
@@ -178,6 +300,13 b' class queue:'
178 300 message.insert(0, subject)
179 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 310 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
182 311 # first try just applying the patch
183 312 (err, n) = self.apply(repo, [ patch ], update_status=False,
@@ -187,35 +316,31 b' class queue:'
187 316 return (err, n)
188 317
189 318 if n is None:
190 self.ui.warn("apply failed for patch %s\n" % patch)
191 sys.exit(1)
319 raise util.Abort(_("apply failed for patch %s") % patch)
192 320
193 321 self.ui.warn("patch didn't work out, merging %s\n" % patch)
194 322
195 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 325 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
198 326
199 327 c = repo.changelog.read(rev)
200 ret = repo.update(rev, allow=True, wlock=wlock)
328 ret = hg.merge(repo, rev, wlock=wlock)
201 329 if ret:
202 self.ui.warn("update returned %d\n" % ret)
203 sys.exit(1)
330 raise util.Abort(_("update returned %d") % ret)
204 331 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
205 332 if n == None:
206 self.ui.warn("repo commit failed\n")
207 sys.exit(1)
333 raise util.Abort(_("repo commit failed"))
208 334 try:
209 335 message, comments, user, date, patchfound = mergeq.readheaders(patch)
210 336 except:
211 self.ui.warn("Unable to read %s\n" % patch)
212 sys.exit(1)
337 raise util.Abort(_("unable to read %s") % patch)
213 338
214 339 patchf = self.opener(patch, "w")
215 340 if comments:
216 341 comments = "\n".join(comments) + '\n\n'
217 342 patchf.write(comments)
218 commands.dodiff(patchf, self.ui, repo, head, n)
343 self.printdiff(repo, head, n, fp=patchf)
219 344 patchf.close()
220 345 return (0, n)
221 346
@@ -226,12 +351,10 b' class queue:'
226 351 return p1
227 352 if len(self.applied) == 0:
228 353 return None
229 (top, patch) = self.applied[-1].split(':')
230 top = revlog.bin(top)
231 return top
354 return revlog.bin(self.applied[-1].rev)
232 355 pp = repo.changelog.parents(rev)
233 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 358 p0 = revlog.hex(pp[0])
236 359 p1 = revlog.hex(pp[1])
237 360 if p0 in arevs:
@@ -251,17 +374,20 b' class queue:'
251 374 pname = ".hg.patches.merge.marker"
252 375 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
253 376 wlock=wlock)
254 self.applied.append(revlog.hex(n) + ":" + pname)
377 self.applied.append(statusentry(revlog.hex(n), pname))
255 378 self.applied_dirty = 1
256 379
257 380 head = self.qparents(repo)
258 381
259 382 for patch in series:
260 patch = mergeq.lookup(patch)
383 patch = mergeq.lookup(patch, strict=True)
261 384 if not patch:
262 385 self.ui.warn("patch %s does not exist\n" % patch)
263 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 391 info = mergeq.isapplied(patch)
266 392 if not info:
267 393 self.ui.warn("patch %s is not applied\n" % patch)
@@ -269,102 +395,80 b' class queue:'
269 395 rev = revlog.bin(info[1])
270 396 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
271 397 if head:
272 self.applied.append(revlog.hex(head) + ":" + patch)
398 self.applied.append(statusentry(revlog.hex(head), patch))
273 399 self.applied_dirty = 1
274 400 if err:
275 401 return (err, head)
276 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 418 def apply(self, repo, series, list=False, update_status=True,
279 419 strict=False, patchdir=None, merge=None, wlock=None):
280 420 # TODO unify with commands.py
281 421 if not patchdir:
282 422 patchdir = self.path
283 pwd = os.getcwd()
284 os.chdir(repo.root)
285 423 err = 0
286 424 if not wlock:
287 425 wlock = repo.wlock()
288 426 lock = repo.lock()
289 427 tr = repo.transaction()
290 428 n = None
291 for patch in series:
292 self.ui.warn("applying %s\n" % patch)
293 pf = os.path.join(patchdir, patch)
429 for patchname in series:
430 pushable, reason = self.pushable(patchname)
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 437 try:
296 message, comments, user, date, patchfound = self.readheaders(patch)
438 message, comments, user, date, patchfound = self.readheaders(patchname)
297 439 except:
298 self.ui.warn("Unable to read %s\n" % pf)
440 self.ui.warn("Unable to read %s\n" % patchname)
299 441 err = 1
300 442 break
301 443
302 444 if not message:
303 message = "imported patch %s\n" % patch
445 message = "imported patch %s\n" % patchname
304 446 else:
305 447 if list:
306 message.append("\nimported patch %s" % patch)
448 message.append("\nimported patch %s" % patchname)
307 449 message = '\n'.join(message)
308 450
309 try:
310 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
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()
451 (patcherr, files, fuzz) = self.patch(repo, pf)
452 patcherr = not patcherr
346 453
347 if merge and len(files) > 0:
454 if merge and files:
348 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 457 p1, p2 = repo.dirstate.parents()
351 458 repo.dirstate.setparents(p1, merge)
352 if len(files) > 0:
353 commands.addremove_lock(self.ui, repo, files,
354 opts={}, wlock=wlock)
459 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
355 460 n = repo.commit(files, message, user, date, force=1, lock=lock,
356 461 wlock=wlock)
357 462
358 463 if n == None:
359 self.ui.warn("repo commit failed\n")
360 sys.exit(1)
464 raise util.Abort(_("repo commit failed"))
361 465
362 466 if update_status:
363 self.applied.append(revlog.hex(n) + ":" + patch)
467 self.applied.append(statusentry(revlog.hex(n), patchname))
364 468
365 469 if patcherr:
366 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 472 err = 0
369 473 else:
370 474 self.ui.warn("patch failed, rejects left in working dir\n")
@@ -376,49 +480,58 b' class queue:'
376 480 err = 1
377 481 break
378 482 tr.close()
379 os.chdir(pwd)
380 483 return (err, n)
381 484
382 def delete(self, repo, patch):
383 patch = self.lookup(patch)
384 info = self.isapplied(patch)
385 if info:
386 self.ui.warn("cannot delete applied patch %s\n" % patch)
387 sys.exit(1)
388 if patch not in self.series:
389 self.ui.warn("patch %s not in series file\n" % patch)
390 sys.exit(1)
391 i = self.find_series(patch)
392 del self.full_series[i]
393 self.read_series(self.full_series)
485 def delete(self, repo, patches, keep=False):
486 realpatches = []
487 for patch in patches:
488 patch = self.lookup(patch, strict=True)
489 info = self.isapplied(patch)
490 if info:
491 raise util.Abort(_("cannot delete applied patch %s") % patch)
492 if patch not in self.series:
493 raise util.Abort(_("patch %s not in series file") % patch)
494 realpatches.append(patch)
495
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 508 self.series_dirty = 1
395 509
396 510 def check_toppatch(self, repo):
397 511 if len(self.applied) > 0:
398 (top, patch) = self.applied[-1].split(':')
399 top = revlog.bin(top)
512 top = revlog.bin(self.applied[-1].rev)
400 513 pp = repo.dirstate.parents()
401 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])))
403 sys.exit(1)
515 raise util.Abort(_("queue top not at same revision as working directory"))
404 516 return top
405 517 return None
406 def check_localchanges(self, repo):
407 (c, a, r, d, u) = repo.changes(None, None)
408 if c or a or d or r:
409 self.ui.write("Local changes found, refresh first\n")
410 sys.exit(1)
518 def check_localchanges(self, repo, force=False, refresh=True):
519 m, a, r, d = repo.status()[:4]
520 if m or a or r or d:
521 if not force:
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 527 def new(self, repo, patch, msg=None, force=None):
412 commitfiles = []
413 (c, a, r, d, u) = repo.changes(None, None)
414 if c or a or d or r:
415 if not force:
416 raise util.Abort(_("Local changes found, refresh first"))
417 else:
418 commitfiles = c + a + r
528 if os.path.exists(self.join(patch)):
529 raise util.Abort(_('patch "%s" already exists') % patch)
530 m, a, r, d = self.check_localchanges(repo, force)
531 commitfiles = m + a + r
419 532 self.check_toppatch(repo)
420 533 wlock = repo.wlock()
421 insert = self.series_end()
534 insert = self.full_series_end()
422 535 if msg:
423 536 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
424 537 wlock=wlock)
@@ -426,11 +539,10 b' class queue:'
426 539 n = repo.commit(commitfiles,
427 540 "New patch: %s" % patch, force=True, wlock=wlock)
428 541 if n == None:
429 self.ui.warn("repo commit failed\n")
430 sys.exit(1)
542 raise util.Abort(_("repo commit failed"))
431 543 self.full_series[insert:insert] = [patch]
432 self.applied.append(revlog.hex(n) + ":" + patch)
433 self.read_series(self.full_series)
544 self.applied.append(statusentry(revlog.hex(n), patch))
545 self.parse_series()
434 546 self.series_dirty = 1
435 547 self.applied_dirty = 1
436 548 p = self.opener(patch, "w")
@@ -509,9 +621,9 b' class queue:'
509 621 # we go in two steps here so the strip loop happens in a
510 622 # sensible order. When stripping many files, this helps keep
511 623 # our disk access patterns under control.
512 list = seen.keys()
513 list.sort()
514 for f in list:
624 seen_list = seen.keys()
625 seen_list.sort()
626 for f in seen_list:
515 627 ff = repo.file(f)
516 628 filerev = seen[f]
517 629 if filerev != 0:
@@ -530,8 +642,9 b' class queue:'
530 642 revnum = chlog.rev(rev)
531 643
532 644 if update:
645 self.check_localchanges(repo, refresh=False)
533 646 urev = self.qparents(repo, rev)
534 repo.update(urev, allow=False, force=True, wlock=wlock)
647 hg.clean(repo, urev, wlock=wlock)
535 648 repo.dirstate.write()
536 649
537 650 # save is a list of all the branches we are truncating away
@@ -540,7 +653,6 b' class queue:'
540 653 saveheads = []
541 654 savebases = {}
542 655
543 tip = chlog.tip()
544 656 heads = limitheads(chlog, rev)
545 657 seen = {}
546 658
@@ -571,7 +683,7 b' class queue:'
571 683 savebases[x] = 1
572 684
573 685 # create a changegroup for all the branches we need to keep
574 if backup is "all":
686 if backup == "all":
575 687 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
576 688 bundle(backupch)
577 689 if saveheads:
@@ -586,37 +698,89 b' class queue:'
586 698 if saveheads:
587 699 self.ui.status("adding branch\n")
588 700 commands.unbundle(self.ui, repo, chgrpfile, update=False)
589 if backup is not "strip":
701 if backup != "strip":
590 702 os.unlink(chgrpfile)
591 703
592 704 def isapplied(self, patch):
593 705 """returns (index, rev, patch)"""
594 706 for i in xrange(len(self.applied)):
595 p = self.applied[i]
596 a = p.split(':')
597 if a[1] == patch:
598 return (i, a[0], a[1])
707 a = self.applied[i]
708 if a.name == patch:
709 return (i, a.rev, a.name)
599 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 738 if patch == None:
603 739 return None
604 if patch in self.series:
605 return patch
606 if not os.path.isfile(os.path.join(self.path, patch)):
740
741 # we don't want to return a partial match until we make
742 # sure the file name passed in does not exist (checked below)
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 748 try:
608 749 sno = int(patch)
609 750 except(ValueError, OverflowError):
610 self.ui.warn("patch %s not in series\n" % patch)
611 sys.exit(1)
612 if sno >= len(self.series):
613 self.ui.warn("patch number %d is out of range\n" % sno)
614 sys.exit(1)
615 patch = self.series[sno]
616 else:
617 self.ui.warn("patch %s not in series\n" % patch)
618 sys.exit(1)
619 return patch
751 pass
752 else:
753 if sno < len(self.series):
754 return self.series[sno]
755 if not strict:
756 # return any partial match made above
757 if res:
758 return res
759 minus = patch.rsplit('-', 1)
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 785 def push(self, repo, patch=None, force=False, list=False,
622 786 mergeq=None, wlock=None):
@@ -624,10 +788,10 b' class queue:'
624 788 wlock = repo.wlock()
625 789 patch = self.lookup(patch)
626 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 792 sys.exit(1)
629 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 795 sys.exit(1)
632 796 if not force:
633 797 self.check_localchanges(repo)
@@ -646,7 +810,7 b' class queue:'
646 810 ret = self.mergepatch(repo, mergeq, s, wlock)
647 811 else:
648 812 ret = self.apply(repo, s, list, wlock=wlock)
649 top = self.applied[-1].split(':')[1]
813 top = self.applied[-1].name
650 814 if ret[0]:
651 815 self.ui.write("Errors during apply, please fix and refresh %s\n" %
652 816 top)
@@ -654,7 +818,8 b' class queue:'
654 818 self.ui.write("Now at: %s\n" % top)
655 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 823 def getfile(f, rev):
659 824 t = repo.file(f).read(rev)
660 825 try:
@@ -675,15 +840,14 b' class queue:'
675 840 patch = self.lookup(patch)
676 841 info = self.isapplied(patch)
677 842 if not info:
678 self.ui.warn("patch %s is not applied\n" % patch)
679 sys.exit(1)
843 raise util.Abort(_("patch %s is not applied") % patch)
680 844 if len(self.applied) == 0:
681 self.ui.warn("No patches applied\n")
845 self.ui.warn(_("no patches applied\n"))
682 846 sys.exit(1)
683 847
684 848 if not update:
685 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 851 for p in parents:
688 852 if p in rr:
689 853 self.ui.warn("qpop: forcing dirstate update\n")
@@ -695,7 +859,17 b' class queue:'
695 859 self.applied_dirty = 1;
696 860 end = len(self.applied)
697 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 873 start = info[0]
700 874 rev = revlog.bin(info[1])
701 875
@@ -705,17 +879,16 b' class queue:'
705 879 top = self.check_toppatch(repo)
706 880 qp = self.qparents(repo, rev)
707 881 changes = repo.changelog.read(qp)
708 mf1 = repo.manifest.readflags(changes[0])
709 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 884 if d:
712 885 raise util.Abort("deletions found between repo revs")
713 for f in c:
886 for f in m:
714 887 getfile(f, mmap[f])
715 888 for f in r:
716 889 getfile(f, mmap[f])
717 util.set_exec(repo.wjoin(f), mf1[f])
718 repo.dirstate.update(c + r, 'n')
890 util.set_exec(repo.wjoin(f), mmap.execf(f))
891 repo.dirstate.update(m + r, 'n')
719 892 for f in a:
720 893 try: os.unlink(repo.wjoin(f))
721 894 except: raise
@@ -727,36 +900,46 b' class queue:'
727 900 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
728 901 del self.applied[start:end]
729 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 904 else:
732 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 908 top = self.check_toppatch(repo)
736 909 if not top:
737 910 self.ui.write("No patches applied\n")
738 911 return
739 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 916 if len(self.applied) == 0:
744 917 self.ui.write("No patches applied\n")
745 918 return
746 919 wlock = repo.wlock()
747 920 self.check_toppatch(repo)
748 qp = self.qparents(repo)
749 (top, patch) = self.applied[-1].split(':')
921 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
750 922 top = revlog.bin(top)
751 923 cparents = repo.changelog.parents(top)
752 924 patchparent = self.qparents(repo, top)
753 925 message, comments, user, date, patchfound = self.readheaders(patch)
754 926
755 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 938 if comments:
757 939 comments = "\n".join(comments) + '\n\n'
758 940 patchf.write(comments)
759 941
942 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
760 943 tip = repo.changelog.tip()
761 944 if top == tip:
762 945 # if the top of our patch queue is also the tip, there is an
@@ -769,30 +952,30 b' class queue:'
769 952 # patch already
770 953 #
771 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 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)
777 if short:
778 filelist = cc + aa + dd
959 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
960 if opts.get('short'):
961 filelist = mm + aa + dd
779 962 else:
780 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 966 # we might end up with files that were added between tip and
784 967 # the dirstate parent, but then changed in the local dirstate.
785 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 970 if x not in aa:
788 cc.append(x)
971 mm.append(x)
789 972 # we might end up with files added by the local dirstate that
790 973 # were deleted by the patch. In this case, they should only
791 974 # show up in the changed section.
792 975 for x in a:
793 976 if x in dd:
794 977 del dd[dd.index(x)]
795 cc.append(x)
978 mm.append(x)
796 979 else:
797 980 aa.append(x)
798 981 # make sure any files deleted in the local dirstate
@@ -803,70 +986,97 b' class queue:'
803 986 del aa[aa.index(x)]
804 987 forget.append(x)
805 988 continue
806 elif x in cc:
807 del cc[cc.index(x)]
989 elif x in mm:
990 del mm[mm.index(x)]
808 991 dd.append(x)
809 992
810 c = list(util.unique(cc))
993 m = list(util.unique(mm))
811 994 r = list(util.unique(dd))
812 995 a = list(util.unique(aa))
813 filelist = list(util.unique(c + r + a ))
814 commands.dodiff(patchf, self.ui, repo, patchparent, None,
815 filelist, changes=(c, a, r, [], u))
996 filelist = filter(matchfn, util.unique(m + r + a))
997 self.printdiff(repo, patchparent, files=filelist,
998 changes=(m, a, r, [], u), fp=patchf)
816 999 patchf.close()
817 1000
818 1001 changes = repo.changelog.read(tip)
819 1002 repo.dirstate.setparents(*cparents)
1003 copies = [(f, repo.dirstate.copied(f)) for f in a]
820 1004 repo.dirstate.update(a, 'a')
1005 for dst, src in copies:
1006 repo.dirstate.copy(src, dst)
821 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 1017 repo.dirstate.forget(forget)
824 1018
825 if not message:
826 message = "patch queue: %s\n" % patch
1019 if not msg:
1020 if not message:
1021 message = "patch queue: %s\n" % patch
1022 else:
1023 message = "\n".join(message)
827 1024 else:
828 message = "\n".join(message)
1025 message = msg
1026
829 1027 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
830 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 1030 self.applied_dirty = 1
833 1031 else:
834 commands.dodiff(patchf, self.ui, repo, patchparent, None)
1032 self.printdiff(repo, patchparent, fp=patchf)
835 1033 patchf.close()
836 1034 self.pop(repo, force=True, wlock=wlock)
837 1035 self.push(repo, force=True, wlock=wlock)
838 1036
839 1037 def init(self, repo, create=False):
840 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 1040 os.mkdir(self.path)
843 1041 if create:
844 1042 return self.qrepo(create=True)
845 1043
846 1044 def unapplied(self, repo, patch=None):
847 1045 if patch and patch not in self.series:
848 self.ui.warn("%s not in the series file\n" % patch)
849 sys.exit(1)
1046 raise util.Abort(_("patch %s is not in series file") % patch)
850 1047 if not patch:
851 1048 start = self.series_end()
852 1049 else:
853 1050 start = self.series.index(patch) + 1
854 for p in self.series[start:]:
855 self.ui.write("%s\n" % p)
1051 unapplied = []
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):
858 start = self.series_end()
1059 def qseries(self, repo, missing=None, summary=False):
1060 start = self.series_end(all_patches=True)
859 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 1064 if self.ui.verbose:
862 self.ui.write("%d A " % self.series.index(p))
863 self.ui.write("%s\n" % p)
864 for p in self.series[start:]:
865 if self.ui.verbose:
866 self.ui.write("%d U " % self.series.index(p))
867 self.ui.write("%s\n" % p)
1065 if i < start:
1066 status = 'A'
1067 elif self.pushable(i)[0]:
1068 status = 'U'
1069 else:
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 1078 else:
869 list = []
1079 msng_list = []
870 1080 for root, dirs, files in os.walk(self.path):
871 1081 d = root[len(self.path) + 1:]
872 1082 for f in files:
@@ -874,21 +1084,19 b' class queue:'
874 1084 if (fl not in self.series and
875 1085 fl not in (self.status_path, self.series_path)
876 1086 and not fl.startswith('.')):
877 list.append(fl)
878 list.sort()
879 if list:
880 for x in list:
881 if self.ui.verbose:
882 self.ui.write("D ")
883 self.ui.write("%s\n" % x)
1087 msng_list.append(fl)
1088 msng_list.sort()
1089 for x in msng_list:
1090 if self.ui.verbose:
1091 self.ui.write("D ")
1092 self.ui.write("%s\n" % x)
884 1093
885 1094 def issaveline(self, l):
886 name = l.split(':')[1]
887 if name == '.hg.patches.save.line':
1095 if l.name == '.hg.patches.save.line':
888 1096 return True
889 1097
890 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 1100 return hg.repository(self.ui, path=self.path, create=create)
893 1101
894 1102 def restore(self, repo, rev, delete=None, qupdate=None):
@@ -909,19 +1117,18 b' class queue:'
909 1117 qpp = [ hg.bin(x) for x in l ]
910 1118 elif datastart != None:
911 1119 l = lines[i].rstrip()
912 index = l.index(':')
913 id = l[:index]
914 file = l[index + 1:]
915 if id:
916 applied.append(l)
917 series.append(file)
1120 se = statusentry(l)
1121 file_ = se.name
1122 if se.rev:
1123 applied.append(se)
1124 series.append(file_)
918 1125 if datastart == None:
919 1126 self.ui.warn("No saved patch data found\n")
920 1127 return 1
921 1128 self.ui.warn("restoring status: %s\n" % lines[0])
922 1129 self.full_series = series
923 1130 self.applied = applied
924 self.read_series(self.full_series)
1131 self.parse_series()
925 1132 self.series_dirty = 1
926 1133 self.applied_dirty = 1
927 1134 heads = repo.changelog.heads()
@@ -945,7 +1152,7 b' class queue:'
945 1152 if not r:
946 1153 self.ui.warn("Unable to load queue repository\n")
947 1154 return 1
948 r.update(qpp[0], allow=False, force=True)
1155 hg.clean(r, qpp[0])
949 1156
950 1157 def save(self, repo, msg=None):
951 1158 if len(self.applied) == 0:
@@ -965,30 +1172,49 b' class queue:'
965 1172 pp = r.dirstate.parents()
966 1173 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
967 1174 msg += "\n\nPatch Data:\n"
968 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
969 + '\n' or "")
1175 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1176 "\n".join(ar) + '\n' or "")
970 1177 n = repo.commit(None, text, user=None, force=1)
971 1178 if not n:
972 1179 self.ui.warn("repo commit failed\n")
973 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 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 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 1206 if len(self.applied) > 0:
980 (top, p) = self.applied[-1].split(':')
1207 p = self.applied[-1].name
981 1208 try:
982 1209 end = self.series.index(p)
983 1210 except ValueError:
984 1211 return 0
985 return end + 1
986 return end
1212 return next(end + 1)
1213 return next(end)
987 1214
988 1215 def qapplied(self, repo, patch=None):
989 1216 if patch and patch not in self.series:
990 self.ui.warn("%s not in the series file\n" % patch)
991 sys.exit(1)
1217 raise util.Abort(_("patch %s is not in series file") % patch)
992 1218 if not patch:
993 1219 end = len(self.applied)
994 1220 else:
@@ -998,9 +1224,11 b' class queue:'
998 1224 self.ui.write("%s\n" % p)
999 1225
1000 1226 def appliedname(self, index):
1001 p = self.applied[index]
1227 pname = self.applied[index].name
1002 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 1232 return p
1005 1233
1006 1234 def top(self, repo):
@@ -1015,7 +1243,10 b' class queue:'
1015 1243 if end == len(self.series):
1016 1244 self.ui.write("All patches applied\n")
1017 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 1251 def prev(self, repo):
1021 1252 if len(self.applied) > 1:
@@ -1028,36 +1259,33 b' class queue:'
1028 1259
1029 1260 def qimport(self, repo, files, patch=None, existing=None, force=None):
1030 1261 if len(files) > 1 and patch:
1031 self.ui.warn("-n option not valid when importing multiple files\n")
1032 sys.exit(1)
1262 raise util.Abort(_('option "-n" not valid when importing multiple '
1263 'files'))
1033 1264 i = 0
1034 1265 added = []
1035 1266 for filename in files:
1036 1267 if existing:
1037 1268 if not patch:
1038 1269 patch = filename
1039 if not os.path.isfile(os.path.join(self.path, patch)):
1040 self.ui.warn("patch %s does not exist\n" % patch)
1041 sys.exit(1)
1270 if not os.path.isfile(self.join(patch)):
1271 raise util.Abort(_("patch %s does not exist") % patch)
1042 1272 else:
1043 1273 try:
1044 1274 text = file(filename).read()
1045 1275 except IOError:
1046 self.ui.warn("Unable to read %s\n" % patch)
1047 sys.exit(1)
1276 raise util.Abort(_("unable to read %s") % patch)
1048 1277 if not patch:
1049 1278 patch = os.path.split(filename)[1]
1050 if not force and os.path.isfile(os.path.join(self.path, patch)):
1051 self.ui.warn("patch %s already exists\n" % patch)
1052 sys.exit(1)
1279 if not force and os.path.exists(self.join(patch)):
1280 raise util.Abort(_('patch "%s" already exists') % patch)
1053 1281 patchf = self.opener(patch, "w")
1054 1282 patchf.write(text)
1055 1283 if patch in self.series:
1056 self.ui.warn("patch %s is already in the series file\n" % patch)
1057 sys.exit(1)
1058 index = self.series_end() + i
1284 raise util.Abort(_('patch %s is already in the series file')
1285 % patch)
1286 index = self.full_series_end() + i
1059 1287 self.full_series[index:index] = [patch]
1060 self.read_series(self.full_series)
1288 self.parse_series()
1061 1289 self.ui.warn("adding %s to series file\n" % patch)
1062 1290 i += 1
1063 1291 added.append(patch)
@@ -1067,34 +1295,44 b' class queue:'
1067 1295 if qrepo:
1068 1296 qrepo.add(added)
1069 1297
1070 def delete(ui, repo, patch, **opts):
1071 """remove a patch from the series file"""
1072 q = repomap[repo]
1073 q.delete(repo, patch)
1298 def delete(ui, repo, patch, *patches, **opts):
1299 """remove patches from queue
1300
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 1305 q.save_dirty()
1075 1306 return 0
1076 1307
1077 1308 def applied(ui, repo, patch=None, **opts):
1078 1309 """print the patches already applied"""
1079 repomap[repo].qapplied(repo, patch)
1310 repo.mq.qapplied(repo, patch)
1080 1311 return 0
1081 1312
1082 1313 def unapplied(ui, repo, patch=None, **opts):
1083 1314 """print the patches not yet applied"""
1084 repomap[repo].unapplied(repo, patch)
1085 return 0
1315 for i, p in repo.mq.unapplied(repo, patch):
1316 if ui.verbose:
1317 ui.write("%d " % i)
1318 ui.write("%s\n" % p)
1086 1319
1087 1320 def qimport(ui, repo, *filename, **opts):
1088 1321 """import a patch"""
1089 q = repomap[repo]
1322 q = repo.mq
1090 1323 q.qimport(repo, filename, patch=opts['name'],
1091 1324 existing=opts['existing'], force=opts['force'])
1092 1325 q.save_dirty()
1093 1326 return 0
1094 1327
1095 1328 def init(ui, repo, **opts):
1096 """init a new queue repository"""
1097 q = repomap[repo]
1329 """init a new queue repository
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 1336 r = q.init(repo, create=opts['create_repo'])
1099 1337 q.save_dirty()
1100 1338 if r:
@@ -1106,68 +1344,254 b' def init(ui, repo, **opts):'
1106 1344 r.add(['.hgignore', 'series'])
1107 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 1391 def commit(ui, repo, *pats, **opts):
1110 1392 """commit changes in the queue repository"""
1111 q = repomap[repo]
1393 q = repo.mq
1112 1394 r = q.qrepo()
1113 1395 if not r: raise util.Abort('no queue repository')
1114 1396 commands.commit(r.ui, r, *pats, **opts)
1115 1397
1116 1398 def series(ui, repo, **opts):
1117 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 1401 return 0
1120 1402
1121 1403 def top(ui, repo, **opts):
1122 1404 """print the name of the current patch"""
1123 repomap[repo].top(repo)
1405 repo.mq.top(repo)
1124 1406 return 0
1125 1407
1126 1408 def next(ui, repo, **opts):
1127 1409 """print the name of the next patch"""
1128 repomap[repo].next(repo)
1410 repo.mq.next(repo)
1129 1411 return 0
1130 1412
1131 1413 def prev(ui, repo, **opts):
1132 1414 """print the name of the previous patch"""
1133 repomap[repo].prev(repo)
1415 repo.mq.prev(repo)
1134 1416 return 0
1135 1417
1136 1418 def new(ui, repo, patch, **opts):
1137 """create a new patch"""
1138 q = repomap[repo]
1139 q.new(repo, patch, msg=opts['message'], force=opts['force'])
1419 """create a new patch
1420
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 1453 q.save_dirty()
1141 1454 return 0
1142 1455
1143 def refresh(ui, repo, **opts):
1144 """update the current patch"""
1145 q = repomap[repo]
1146 q.refresh(repo, short=opts['short'])
1147 q.save_dirty()
1456 def diff(ui, repo, *pats, **opts):
1457 """diff of the current patch"""
1458 repo.mq.diff(repo, pats, opts)
1148 1459 return 0
1149 1460
1150 def diff(ui, repo, *files, **opts):
1151 """diff of the current patch"""
1152 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1153 repomap[repo].diff(repo, list(files))
1154 return 0
1461 def fold(ui, repo, *files, **opts):
1462 """fold the named patches into the current patch
1463
1464 Patches must not yet be applied. Each patch will be successively
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 1580 def lastsavename(path):
1157 (dir, base) = os.path.split(path)
1158 names = os.listdir(dir)
1581 (directory, base) = os.path.split(path)
1582 names = os.listdir(directory)
1159 1583 namere = re.compile("%s.([0-9]+)" % base)
1160 max = None
1584 maxindex = None
1161 1585 maxname = None
1162 1586 for f in names:
1163 1587 m = namere.match(f)
1164 1588 if m:
1165 1589 index = int(m.group(1))
1166 if max == None or index > max:
1167 max = index
1590 if maxindex == None or index > maxindex:
1591 maxindex = index
1168 1592 maxname = f
1169 1593 if maxname:
1170 return (os.path.join(dir, maxname), max)
1594 return (os.path.join(directory, maxname), maxindex)
1171 1595 return (None, None)
1172 1596
1173 1597 def savename(path):
@@ -1179,7 +1603,7 b' def savename(path):'
1179 1603
1180 1604 def push(ui, repo, patch=None, **opts):
1181 1605 """push the next patch onto the stack"""
1182 q = repomap[repo]
1606 q = repo.mq
1183 1607 mergeq = None
1184 1608
1185 1609 if opts['all']:
@@ -1207,17 +1631,65 b' def pop(ui, repo, patch=None, **opts):'
1207 1631 ui.warn('using patch queue: %s\n' % q.path)
1208 1632 localupdate = False
1209 1633 else:
1210 q = repomap[repo]
1211 if opts['all'] and len(q.applied) > 0:
1212 patch = q.applied[0].split(':')[1]
1213 q.pop(repo, patch, force=opts['force'], update=localupdate)
1634 q = repo.mq
1635 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1214 1636 q.save_dirty()
1215 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 1689 def restore(ui, repo, rev, **opts):
1218 1690 """restore the queue state saved by a rev"""
1219 1691 rev = repo.lookup(rev)
1220 q = repomap[repo]
1692 q = repo.mq
1221 1693 q.restore(repo, rev, delete=opts['delete'],
1222 1694 qupdate=opts['update'])
1223 1695 q.save_dirty()
@@ -1225,8 +1697,9 b' def restore(ui, repo, rev, **opts):'
1225 1697
1226 1698 def save(ui, repo, **opts):
1227 1699 """save current queue state"""
1228 q = repomap[repo]
1229 ret = q.save(repo, msg=opts['message'])
1700 q = repo.mq
1701 message = commands.logmessage(opts)
1702 ret = q.save(repo, msg=message)
1230 1703 if ret:
1231 1704 return ret
1232 1705 q.save_dirty()
@@ -1236,20 +1709,18 b' def save(ui, repo, **opts):'
1236 1709 newpath = os.path.join(q.basepath, opts['name'])
1237 1710 if os.path.exists(newpath):
1238 1711 if not os.path.isdir(newpath):
1239 ui.warn("destination %s exists and is not a directory\n" %
1240 newpath)
1241 sys.exit(1)
1712 raise util.Abort(_('destination %s exists and is not '
1713 'a directory') % newpath)
1242 1714 if not opts['force']:
1243 ui.warn("destination %s exists, use -f to force\n" %
1244 newpath)
1245 sys.exit(1)
1715 raise util.Abort(_('destination %s exists, '
1716 'use -f to force') % newpath)
1246 1717 else:
1247 1718 newpath = savename(path)
1248 1719 ui.warn("copy %s to %s\n" % (path, newpath))
1249 1720 util.copyfiles(path, newpath)
1250 1721 if opts['empty']:
1251 1722 try:
1252 os.unlink(os.path.join(q.path, q.status_path))
1723 os.unlink(q.join(q.status_path))
1253 1724 except:
1254 1725 pass
1255 1726 return 0
@@ -1262,25 +1733,196 b' def strip(ui, repo, rev, **opts):'
1262 1733 backup = 'strip'
1263 1734 elif opts['nobackup']:
1264 1735 backup = 'none'
1265 repomap[repo].strip(repo, rev, backup=backup)
1736 repo.mq.strip(repo, rev, backup=backup)
1266 1737 return 0
1267 1738
1268 def version(ui, q=None):
1269 """print the version number"""
1270 ui.write("mq version %s\n" % versionstr)
1271 return 0
1739 def select(ui, repo, *args, **opts):
1740 '''set or print guarded patches to push
1741
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 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 1890 cmdtable = {
1277 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 1902 "qcommit|qci":
1279 1903 (commit,
1280 1904 commands.table["^commit|ci"][1],
1281 1905 'hg qcommit [OPTION]... [FILE]...'),
1282 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1283 "qdelete": (delete, [], 'hg qdelete PATCH'),
1906 "^qdiff": (diff,
1907 [('I', 'include', [], _('include names matching the given patterns')),
1908 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1909 'hg qdiff [-I] [-X] [FILE]...'),
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 1926 "^qimport":
1285 1927 (qimport,
1286 1928 [('e', 'existing', None, 'import file in patch dir'),
@@ -1293,9 +1935,11 b' cmdtable = {'
1293 1935 'hg qinit [-c]'),
1294 1936 "qnew":
1295 1937 (new,
1296 [('m', 'message', '', 'commit message'),
1297 ('f', 'force', None, 'force')],
1298 'hg qnew [-m TEXT] [-f] PATCH'),
1938 [('e', 'edit', None, _('edit commit message')),
1939 ('m', 'message', '', _('use <text> as commit message')),
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 1943 "qnext": (next, [], 'hg qnext'),
1300 1944 "qprev": (prev, [], 'hg qprev'),
1301 1945 "^qpop":
@@ -1314,8 +1958,15 b' cmdtable = {'
1314 1958 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1315 1959 "^qrefresh":
1316 1960 (refresh,
1317 [('s', 'short', None, 'short refresh')],
1318 'hg qrefresh [-s]'),
1961 [('e', 'edit', None, _('edit commit message')),
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 1970 "qrestore":
1320 1971 (restore,
1321 1972 [('d', 'delete', None, 'delete save entry'),
@@ -1323,15 +1974,24 b' cmdtable = {'
1323 1974 'hg qrestore [-d] [-u] REV'),
1324 1975 "qsave":
1325 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 1979 ('c', 'copy', None, 'copy patch directory'),
1328 1980 ('n', 'name', '', 'copy directory name'),
1329 1981 ('e', 'empty', None, 'clear queue status file'),
1330 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 1991 "qseries":
1333 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 1995 'hg qseries [-m]'),
1336 1996 "^strip":
1337 1997 (strip,
@@ -1341,6 +2001,4 b' cmdtable = {'
1341 2001 'hg strip [-f] [-b] [-n] REV'),
1342 2002 "qtop": (top, [], 'hg qtop'),
1343 2003 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1344 "qversion": (version, [], 'hg qversion')
1345 2004 }
1346
@@ -67,8 +67,8 b''
67 67 from mercurial.demandload import *
68 68 from mercurial.i18n import gettext as _
69 69 from mercurial.node import *
70 demandload(globals(), 'email.Parser mercurial:commands,templater,util')
71 demandload(globals(), 'fnmatch socket time')
70 demandload(globals(), 'mercurial:commands,patch,templater,util,mail')
71 demandload(globals(), 'email.Parser fnmatch socket time')
72 72
73 73 # template for single changeset can include email headers.
74 74 single_template = '''
@@ -229,8 +229,8 b' class notifier(object):'
229 229 else:
230 230 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
231 231 (len(self.subs), count))
232 mail = self.ui.sendmail()
233 mail.sendmail(templater.email(msg['From']), self.subs, msgtext)
232 mail.sendmail(self.ui, templater.email(msg['From']),
233 self.subs, msgtext)
234 234
235 235 def diff(self, node, ref):
236 236 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
@@ -238,7 +238,7 b' class notifier(object):'
238 238 return
239 239 fp = templater.stringio()
240 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 242 difflines = fp.getvalue().splitlines(1)
243 243 if maxdiff > 0 and len(difflines) > maxdiff:
244 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 255 changegroup. else send one email per changeset.'''
256 256 n = notifier(ui, repo, hooktype)
257 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 259 return
260 260 if n.skipsource(source):
261 261 ui.debug(_('notify: changes have source "%s" - skipping\n') %
@@ -23,27 +23,52 b''
23 23 # the changeset summary, so you can be sure you are sending the right
24 24 # changes.
25 25 #
26 # It is best to run this script with the "-n" (test only) flag before
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.
26 # To enable this extension:
29 27 #
30 # The "-m" (mbox) option will create an mbox file instead of sending
31 # the messages directly. This can be reviewed e.g. with "mutt -R -f mbox",
32 # and finally sent with "formail -s sendmail -bm -t < mbox".
28 # [extensions]
29 # hgext.patchbomb =
33 30 #
34 31 # To configure other defaults, add a section like this to your hgrc
35 32 # file:
36 33 #
37 # [email]
38 # from = My Name <my@email>
39 # to = recipient1, recipient2, ...
40 # cc = cc1, cc2, ...
34 # [email]
35 # from = My Name <my@email>
36 # to = recipient1, recipient2, ...
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 66 from mercurial.demandload import *
43 67 demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
44 mercurial:commands,hg,ui
68 mercurial:commands,hg,mail,ui
45 69 os errno popen2 socket sys tempfile time''')
46 70 from mercurial.i18n import gettext as _
71 from mercurial.node import *
47 72
48 73 try:
49 74 # readline gives raw_input editing capabilities, but is not
@@ -129,8 +154,26 b' def patchbomb(ui, repo, *revs, **opts):'
129 154 while patch and not patch[0].strip(): patch.pop(0)
130 155 if opts['diffstat']:
131 156 body += cdiffstat('\n'.join(desc), patch) + '\n\n'
132 body += '\n'.join(patch)
133 msg = email.MIMEText.MIMEText(body)
157 if opts['attach']:
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 177 if total == 1:
135 178 subj = '[PATCH] ' + desc[0].strip()
136 179 else:
@@ -185,11 +228,14 b' def patchbomb(ui, repo, *revs, **opts):'
185 228 to = getaddrs('to', 'To')
186 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 235 if len(patches) > 1:
189 236 ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
190 237
191 msg = email.MIMEMultipart.MIMEMultipart()
192 msg['Subject'] = '[PATCH 0 of %d] %s' % (
238 subj = '[PATCH 0 of %d] %s' % (
193 239 len(patches),
194 240 opts['subject'] or
195 241 prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches)))
@@ -204,18 +250,21 b' def patchbomb(ui, repo, *revs, **opts):'
204 250 if l == '.': break
205 251 body.append(l)
206 252
207 msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n'))
208
209 253 if opts['diffstat']:
210 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 262 msgs.insert(0, msg)
214 263
215 264 ui.write('\n')
216 265
217 266 if not opts['test'] and not opts['mbox']:
218 mail = ui.sendmail()
267 mailer = mail.connect(ui)
219 268 parent = None
220 269
221 270 # Calculate UTC offset
@@ -240,7 +289,8 b' def patchbomb(ui, repo, *revs, **opts):'
240 289 start_time += 1
241 290 m['From'] = sender
242 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 294 if opts['test']:
245 295 ui.status('Displaying ', m['Subject'], ' ...\n')
246 296 fp = os.popen(os.getenv('PAGER', 'more'), 'w')
@@ -261,12 +311,16 b' def patchbomb(ui, repo, *revs, **opts):'
261 311 fp.close()
262 312 else:
263 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 318 cmdtable = {
267 319 'email':
268 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 324 ('d', 'diffstat', None, 'add diffstat output to messages'),
271 325 ('f', 'from', '', 'email address of sender'),
272 326 ('', 'plain', None, 'omit hg patch header'),
@@ -163,12 +163,12 b' def archive(repo, dest, node, kind, deco'
163 163 change = repo.changelog.read(node)
164 164 mn = change[0]
165 165 archiver = archivers[kind](dest, prefix, mtime or change[2][0])
166 mf = repo.manifest.read(mn).items()
167 mff = repo.manifest.readflags(mn)
168 mf.sort()
166 m = repo.manifest.read(mn)
167 items = m.items()
168 items.sort()
169 169 write('.hg_archival.txt', 0644,
170 170 'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
171 for filename, filenode in mf:
172 write(filename, mff[filename] and 0755 or 0644,
171 for filename, filenode in items:
172 write(filename, m.execf(filename) and 0755 or 0644,
173 173 repo.file(filename).read(filenode))
174 174 archiver.done()
@@ -1,7 +1,7 b''
1 1 /*
2 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 6 This software may be used and distributed according to the terms of
7 7 the GNU General Public License, incorporated herein by reference.
@@ -159,6 +159,10 b' class bundlefilelog(bundlerevlog, filelo'
159 159 class bundlerepository(localrepo.localrepository):
160 160 def __init__(self, ui, path, bundlename):
161 161 localrepo.localrepository.__init__(self, ui, path)
162
163 self._url = 'bundle:' + bundlename
164 if path: self._url += '+' + path
165
162 166 self.tempfile = None
163 167 self.bundlefile = open(bundlename, "rb")
164 168 header = self.bundlefile.read(6)
@@ -208,6 +212,9 b' class bundlerepository(localrepo.localre'
208 212 for c in changegroup.chunkiter(self.bundlefile):
209 213 pass
210 214
215 def url(self):
216 return self._url
217
211 218 def dev(self):
212 219 return -1
213 220
@@ -230,3 +237,18 b' class bundlerepository(localrepo.localre'
230 237 self.bundlefile.close()
231 238 if self.tempfile is not None:
232 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 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 5 # This software may be used and distributed according to the terms
6 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 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -10,10 +10,10 b' from node import *'
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 demandload(globals(), "hgweb.server sshserver")
15 demandload(globals(), "archival cStringIO changegroup")
16 demandload(globals(), "cmdutil hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
@@ -21,47 +21,34 b' class AmbiguousCommand(Exception):'
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 modified, added, removed, deleted, unknown = repo.changes()
24 modified, added, removed, deleted = repo.status()[:4]
25 25 if modified or added or removed or deleted:
26 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 28 def relpath(repo, args):
38 29 cwd = repo.getcwd()
39 30 if cwd:
40 31 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 32 return args
42 33
43 def matchpats(repo, pats=[], opts={}, head=''):
44 cwd = repo.getcwd()
45 if not pats and cwd:
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
48 cwd = ''
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
50 opts.get('exclude'), head)
51
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
54 exact = dict(zip(files, files))
55 def walk():
56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
57 badmatch=badmatch):
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
59 return files, matchfn, walk()
60
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
34 def logmessage(opts):
35 """ get the log message according to -m and -l option """
36 message = opts['message']
37 logfile = opts['logfile']
38
39 if message and logfile:
40 raise util.Abort(_('options --message and --logfile are mutually '
41 'exclusive'))
42 if not message and logfile:
43 try:
44 if logfile == '-':
45 message = sys.stdin.read()
46 else:
47 message = open(logfile).read()
48 except IOError, inst:
49 raise util.Abort(_("can't read commit message '%s': %s") %
50 (logfile, inst.strerror))
51 return message
65 52
66 53 def walkchangerevs(ui, repo, pats, opts):
67 54 '''Iterate over files and the revs they changed in.
@@ -105,12 +92,23 b' def walkchangerevs(ui, repo, pats, opts)'
105 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 98 if repo.changelog.count() == 0:
111 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 112 wanted = {}
115 113 slowpath = anypats
116 114 fncache = {}
@@ -125,37 +123,54 b' def walkchangerevs(ui, repo, pats, opts)'
125 123 if not slowpath and not files:
126 124 # No files, no patterns. Display all revs.
127 125 wanted = dict(zip(revs, revs))
126 copies = []
128 127 if not slowpath:
129 128 # Only files, no patterns. Check the history of each file.
130 def filerevgen(filelog):
129 def filerevgen(filelog, node):
131 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 136 revs = []
134 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 141 revs.reverse()
137 142 for rev in revs:
138 143 # only yield rev for which we have the changelog, it can
139 144 # happen while doing "hg log" during a pull or commit
140 if rev < cl_count:
145 if rev[0] < cl_count:
141 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 152 minrev, maxrev = min(revs), max(revs)
144 for file_ in files:
153 for file_, node in iterfiles():
145 154 filelog = repo.file(file_)
146 155 # A zero count may be a directory or deleted file, so
147 156 # try to find matching entries on the slow path.
148 157 if filelog.count() == 0:
149 158 slowpath = True
150 159 break
151 for rev in filerevgen(filelog):
160 for rev, copied in filerevgen(filelog, node):
152 161 if rev <= maxrev:
153 162 if rev < minrev:
154 163 break
155 164 fncache.setdefault(rev, [])
156 165 fncache[rev].append(file_)
157 166 wanted[rev] = 1
167 if follow and copied:
168 copies.append(copied)
158 169 if slowpath:
170 if follow:
171 raise util.Abort(_('can only follow copies/renames for explicit '
172 'file names'))
173
159 174 # The slow path checks files modified in every changeset.
160 175 def changerevgen():
161 176 for i, window in increasing_windows(repo.changelog.count()-1, -1):
@@ -168,11 +183,66 b' def walkchangerevs(ui, repo, pats, opts)'
168 183 fncache[rev] = matches
169 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 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 243 for i, window in increasing_windows(0, len(revs)):
173 244 yield 'window', revs[0] < revs[-1], revs[-1]
174 nrevs = [rev for rev in revs[i:i+window]
175 if rev in wanted]
245 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
176 246 srevs = list(nrevs)
177 247 srevs.sort()
178 248 for rev in srevs:
@@ -252,62 +322,6 b' def revrange(ui, repo, revs):'
252 322 seen[rev] = 1
253 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 325 def write_bundle(cg, filename=None, compress=True):
312 326 """Write a bundle file and return its filename.
313 327
@@ -360,83 +374,6 b' def write_bundle(cg, filename=None, comp'
360 374 if cleanup is not None:
361 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 377 def trimuser(ui, name, rev, revcache):
441 378 """trim the name of the user who committed a change"""
442 379 user = revcache.get(rev)
@@ -493,7 +430,7 b' class changeset_printer(object):'
493 430 self.ui.status(_("date: %s\n") % date)
494 431
495 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 434 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
498 435 files):
499 436 if value:
@@ -537,12 +474,19 b' def show_changeset(ui, repo, opts):'
537 474 return t
538 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 484 def show_version(ui):
541 485 """output version and copyright information"""
542 486 ui.write(_("Mercurial Distributed SCM (version %s)\n")
543 487 % version.get_version())
544 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 490 "This is free software; see the source for copying conditions. "
547 491 "There is NO\nwarranty; "
548 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 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 644 if exact:
701 645 if ui.verbose:
702 646 ui.status(_('adding %s\n') % rel)
@@ -715,22 +659,7 b' def addremove(ui, repo, *pats, **opts):'
715 659 New files are ignored if they match any of the patterns in .hgignore. As
716 660 with add, these changes take effect at the next commit.
717 661 """
718 return addremove_lock(ui, 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)
662 return cmdutil.addremove(repo, pats, opts)
734 663
735 664 def annotate(ui, repo, *pats, **opts):
736 665 """show changeset information per file line
@@ -773,7 +702,8 b' def annotate(ui, repo, *pats, **opts):'
773 702
774 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 707 fctx = ctx.filectx(abs)
778 708 if not opts['text'] and util.binary(fctx.data()):
779 709 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
@@ -825,10 +755,10 b' def archive(ui, repo, dest, **opts):'
825 755 raise util.Abort(_('uncommitted merge - please provide a '
826 756 'specific revision'))
827 757
828 dest = make_filename(repo, dest, node)
758 dest = cmdutil.make_filename(repo, dest, node)
829 759 if os.path.realpath(dest) == repo.root:
830 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 762 kind = opts.get('type') or 'files'
833 763 prefix = opts['prefix']
834 764 if dest == '-':
@@ -836,7 +766,7 b' def archive(ui, repo, dest, **opts):'
836 766 raise util.Abort(_('cannot archive plain files to stdout'))
837 767 dest = sys.stdout
838 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 770 archival.archive(repo, dest, node, kind, not opts['no_decode'],
841 771 matchfn, prefix)
842 772
@@ -879,7 +809,7 b' def backout(ui, repo, rev, **opts):'
879 809 if opts['parent']:
880 810 raise util.Abort(_('cannot use --parent on non-merge changeset'))
881 811 parent = p1
882 repo.update(node, force=True, show_stats=False)
812 hg.clean(repo, node, show_stats=False)
883 813 revert_opts = opts.copy()
884 814 revert_opts['rev'] = hex(parent)
885 815 revert(ui, repo, **revert_opts)
@@ -896,11 +826,13 b' def backout(ui, repo, rev, **opts):'
896 826 if op1 != node:
897 827 if opts['merge']:
898 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 831 else:
901 832 ui.status(_('the backout changeset is a new head - '
902 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 837 def bundle(ui, repo, fname, dest=None, **opts):
906 838 """create a changegroup file
@@ -937,9 +869,10 b' def cat(ui, repo, file1, *pats, **opts):'
937 869 %d dirname of file being printed, or '.' if in repo root
938 870 %p root-relative path name of file being printed
939 871 """
940 ctx = repo.changectx(opts['rev'] or -1)
941 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
942 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
872 ctx = repo.changectx(opts['rev'] or "-1")
873 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
874 ctx.node()):
875 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
943 876 fp.write(ctx.filectx(abs).data())
944 877
945 878 def clone(ui, source, dest=None, **opts):
@@ -954,10 +887,25 b' def clone(ui, source, dest=None, **opts)'
954 887 .hg/hgrc file, as the default to be used for future pulls.
955 888
956 889 For efficiency, hardlinks are used for cloning whenever the source
957 and destination are on the same filesystem. Some filesystems,
958 such as AFS, implement hardlinking incorrectly, but do not report
959 errors. In these cases, use the --pull option to avoid
960 hardlinking.
890 and destination are on the same filesystem (note this applies only
891 to the repository data, not to the checked out files). Some
892 filesystems, such as AFS, implement hardlinking incorrectly, but
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 910 See pull for valid source format details.
963 911
@@ -965,7 +913,7 b' def clone(ui, source, dest=None, **opts)'
965 913 .hg/hgrc will be created on the remote side. Look at the help text
966 914 for the pull command for important details about ssh:// URLs.
967 915 """
968 ui.setconfig_remoteopts(**opts)
916 setremoteconfig(ui, opts)
969 917 hg.clone(ui, ui.expandpath(source), dest,
970 918 pull=opts['pull'],
971 919 stream=opts['uncompressed'],
@@ -983,28 +931,13 b' def commit(ui, repo, *pats, **opts):'
983 931 If no commit message is specified, the editor configured in your hgrc
984 932 or in the EDITOR environment variable is started to enter a message.
985 933 """
986 message = opts['message']
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))
934 message = logmessage(opts)
1001 935
1002 936 if opts['addremove']:
1003 addremove_lock(ui, repo, pats, opts)
1004 fns, match, anypats = matchpats(repo, pats, opts)
937 cmdutil.addremove(repo, pats, opts)
938 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
1005 939 if pats:
1006 modified, added, removed, deleted, unknown = (
1007 repo.changes(files=fns, match=match))
940 modified, added, removed = repo.status(files=fns, match=match)[:3]
1008 941 files = modified + added + removed
1009 942 else:
1010 943 files = []
@@ -1159,7 +1092,7 b' def docopy(ui, repo, pats, opts, wlock):'
1159 1092 copylist = []
1160 1093 for pat in pats:
1161 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 1096 origsrc = okaytocopy(abssrc, relsrc, exact)
1164 1097 if origsrc:
1165 1098 srcs.append((origsrc, abssrc, relsrc, exact))
@@ -1233,9 +1166,9 b' def debugrebuildstate(ui, repo, rev=None'
1233 1166 rev = repo.lookup(rev)
1234 1167 change = repo.changelog.read(rev)
1235 1168 n = change[0]
1236 files = repo.manifest.readflags(n)
1169 files = repo.manifest.read(n)
1237 1170 wlock = repo.wlock()
1238 repo.dirstate.rebuild(rev, files.iteritems())
1171 repo.dirstate.rebuild(rev, files)
1239 1172
1240 1173 def debugcheckstate(ui, repo):
1241 1174 """validate the correctness of the current dirstate"""
@@ -1376,7 +1309,7 b' def debugrename(ui, repo, file, rev=None'
1376 1309
1377 1310 def debugwalk(ui, repo, *pats, **opts):
1378 1311 """show how files match on given patterns"""
1379 items = list(walk(repo, pats, opts))
1312 items = list(cmdutil.walk(repo, pats, opts))
1380 1313 if not items:
1381 1314 return
1382 1315 fmt = '%%s %%-%ds %%-%ds %%s' % (
@@ -1405,37 +1338,10 b' def diff(ui, repo, *pats, **opts):'
1405 1338 """
1406 1339 node1, node2 = revpair(ui, repo, opts['rev'])
1407 1340
1408 fns, matchfn, anypats = matchpats(repo, pats, opts)
1409
1410 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1411 text=opts['text'], opts=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()
1341 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1342
1343 patch.diff(repo, node1, node2, fns, match=matchfn,
1344 opts=patch.diffopts(ui, opts))
1439 1345
1440 1346 def export(ui, repo, *changesets, **opts):
1441 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 1373 if not changesets:
1468 1374 raise util.Abort(_("export requires at least one changeset"))
1469 seqno = 0
1470 1375 revs = list(revrange(ui, repo, changesets))
1471 total = len(revs)
1472 revwidth = max(map(len, revs))
1473 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1474 ui.note(msg)
1475 for cset in revs:
1476 seqno += 1
1477 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1376 if len(revs) > 1:
1377 ui.note(_('exporting patches:\n'))
1378 else:
1379 ui.note(_('exporting patch:\n'))
1380 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1381 switch_parent=opts['switch_parent'],
1382 opts=patch.diffopts(ui, opts))
1478 1383
1479 1384 def forget(ui, repo, *pats, **opts):
1480 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 1393 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1489 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 1396 if repo.dirstate.state(abs) == 'a':
1492 1397 forget.append(abs)
1493 1398 if ui.verbose or not exact:
@@ -1544,42 +1449,56 b' def grep(ui, repo, pattern, *pats, **opt'
1544 1449 self.linenum = linenum
1545 1450 self.colstart = colstart
1546 1451 self.colend = colend
1452
1547 1453 def __eq__(self, other):
1548 1454 return self.line == other.line
1549 def __hash__(self):
1550 return hash(self.line)
1551 1455
1552 1456 matches = {}
1457 copies = {}
1553 1458 def grepbody(fn, rev, body):
1554 matches[rev].setdefault(fn, {})
1459 matches[rev].setdefault(fn, [])
1555 1460 m = matches[rev][fn]
1556 1461 for lnum, cstart, cend, line in matchlines(body):
1557 1462 s = linestate(line, lnum, cstart, cend)
1558 m[s] = s
1559
1560 # FIXME: prev isn't used, why ?
1463 m.append(s)
1464
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 1480 prev = {}
1562 1481 ucache = {}
1563 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 1483 counts = {'-': 0, '+': 0}
1567 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 1490 if incrementing or not opts['all']:
1570 change = ((l in prevstates) and '-') or '+'
1571 1491 r = rev
1572 1492 else:
1573 change = ((l in states) and '-') or '+'
1574 1493 r = prev[fn]
1575 cols = [fn, str(rev)]
1494 cols = [fn, str(r)]
1576 1495 if opts['line_number']:
1577 1496 cols.append(str(l.linenum))
1578 1497 if opts['all']:
1579 1498 cols.append(change)
1580 1499 if opts['user']:
1581 cols.append(trimuser(ui, getchange(rev)[1], rev,
1582 ucache))
1500 cols.append(trimuser(ui, getchange(r)[1], rev,
1501 ucache))
1583 1502 if opts['files_with_matches']:
1584 1503 c = (fn, rev)
1585 1504 if c in filerevmatches:
@@ -1596,6 +1515,7 b' def grep(ui, repo, pattern, *pats, **opt'
1596 1515 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1597 1516 count = 0
1598 1517 incrementing = False
1518 follow = opts.get('follow')
1599 1519 for st, rev, fns in changeiter:
1600 1520 if st == 'window':
1601 1521 incrementing = rev
@@ -1610,20 +1530,31 b' def grep(ui, repo, pattern, *pats, **opt'
1610 1530 fstate.setdefault(fn, {})
1611 1531 try:
1612 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 1537 except KeyError:
1614 1538 pass
1615 1539 elif st == 'iter':
1616 1540 states = matches[rev].items()
1617 1541 states.sort()
1618 1542 for fn, m in states:
1543 copy = copies.get(rev, {}).get(fn)
1619 1544 if fn in skip:
1545 if copy:
1546 skip[copy] = True
1620 1547 continue
1621 1548 if incrementing or not opts['all'] or fstate[fn]:
1622 1549 pos, neg = display(fn, rev, m, fstate[fn])
1623 1550 count += pos + neg
1624 1551 if pos and not opts['all']:
1625 1552 skip[fn] = True
1553 if copy:
1554 skip[copy] = True
1626 1555 fstate[fn] = m
1556 if copy:
1557 fstate[copy] = m
1627 1558 prev[fn] = rev
1628 1559
1629 1560 if not incrementing:
@@ -1632,7 +1563,8 b' def grep(ui, repo, pattern, *pats, **opt'
1632 1563 for fn, state in fstate:
1633 1564 if fn in skip:
1634 1565 continue
1635 display(fn, rev, {}, state)
1566 if fn not in copies.get(prev[fn], {}):
1567 display(fn, rev, {}, state)
1636 1568 return (count == 0 and 1) or 0
1637 1569
1638 1570 def heads(ui, repo, **opts):
@@ -1670,7 +1602,7 b' def identify(ui, repo):'
1670 1602 return
1671 1603
1672 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 1606 output = ["%s%s" %
1675 1607 ('+'.join([hexfunc(parent) for parent in parents]),
1676 1608 (modified or added or removed or deleted) and "+" or "")]
@@ -1714,81 +1646,23 b' def import_(ui, repo, patch1, *patches, '
1714 1646 d = opts["base"]
1715 1647 strip = opts["strip"]
1716 1648
1717 mailre = re.compile(r'(?:From |[\w-]+:)')
1718
1719 # attempt to detect the start of a patch
1720 # (this heuristic is borrowed from quilt)
1721 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1722 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
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()
1649 wlock = repo.wlock()
1650 lock = repo.lock()
1651
1652 for p in patches:
1653 pf = os.path.join(d, p)
1654
1734 1655 if pf == '-':
1735 msg = p.parse(sys.stdin)
1736 1656 ui.status(_("applying patch from stdin\n"))
1657 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1737 1658 else:
1738 msg = p.parse(file(pf))
1739 ui.status(_("applying %s\n") % patch)
1740
1741 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1742 tmpfp = os.fdopen(fd, 'w')
1659 ui.status(_("applying %s\n") % p)
1660 tmpname, message, user, date = patch.extract(ui, file(pf))
1661
1662 if tmpname is None:
1663 raise util.Abort(_('no diffs found'))
1664
1743 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 1666 if opts['message']:
1793 1667 # pickup the cmdline msg
1794 1668 message = opts['message']
@@ -1800,14 +1674,9 b' def import_(ui, repo, patch1, *patches, '
1800 1674 message = None
1801 1675 ui.debug(_('message:\n%s\n') % message)
1802 1676
1803 tmpfp.close()
1804 if not diffs_seen:
1805 raise util.Abort(_('no diffs found'))
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)
1677 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1678 files = patch.updatedir(ui, repo, files, wlock=wlock)
1679 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1811 1680 finally:
1812 1681 os.unlink(tmpname)
1813 1682
@@ -1824,7 +1693,7 b' def incoming(ui, repo, source="default",'
1824 1693 See pull for valid source format details.
1825 1694 """
1826 1695 source = ui.expandpath(source)
1827 ui.setconfig_remoteopts(**opts)
1696 setremoteconfig(ui, opts)
1828 1697
1829 1698 other = hg.repository(ui, source)
1830 1699 incoming = repo.findincoming(other, force=opts["force"])
@@ -1860,7 +1729,7 b' def incoming(ui, repo, source="default",'
1860 1729 displayer.show(changenode=n)
1861 1730 if opts['patch']:
1862 1731 prev = (parents and parents[0]) or nullid
1863 dodiff(ui, ui, other, prev, n)
1732 patch.diff(repo, other, prev, n)
1864 1733 ui.write("\n")
1865 1734 finally:
1866 1735 if hasattr(other, 'close'):
@@ -1880,7 +1749,7 b' def init(ui, dest=".", **opts):'
1880 1749 Look at the help text for the pull command for important details
1881 1750 about ssh:// URLs.
1882 1751 """
1883 ui.setconfig_remoteopts(**opts)
1752 setremoteconfig(ui, opts)
1884 1753 hg.repository(ui, dest, create=1)
1885 1754
1886 1755 def locate(ui, repo, *pats, **opts):
@@ -1908,8 +1777,8 b' def locate(ui, repo, *pats, **opts):'
1908 1777 else:
1909 1778 node = None
1910 1779
1911 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1912 head='(?:.*/|)'):
1780 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1781 head='(?:.*/|)'):
1913 1782 if not node and repo.dirstate.state(abs) == '?':
1914 1783 continue
1915 1784 if opts['fullpath']:
@@ -1920,7 +1789,18 b' def locate(ui, repo, *pats, **opts):'
1920 1789 def log(ui, repo, *pats, **opts):
1921 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 1805 By default this command outputs: changeset id and hash, tags,
1926 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 1880 displayer.show(rev, brinfo=br)
2001 1881 if opts['patch']:
2002 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 1884 du.write("\n\n")
2005 1885 elif st == 'iter':
2006 1886 if count == limit: break
@@ -2031,22 +1911,44 b' def manifest(ui, repo, rev=None):'
2031 1911 else:
2032 1912 n = repo.manifest.tip()
2033 1913 m = repo.manifest.read(n)
2034 mf = repo.manifest.readflags(n)
2035 1914 files = m.keys()
2036 1915 files.sort()
2037 1916
2038 1917 for f in files:
2039 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2040
2041 def merge(ui, repo, node=None, **opts):
1918 ui.write("%40s %3s %s\n" % (hex(m[f]),
1919 m.execf(f) and "755" or "644", f))
1920
1921 def merge(ui, repo, node=None, force=None, branch=None):
2042 1922 """Merge working directory with another revision
2043 1923
2044 1924 Merge the contents of the current working directory and the
2045 1925 requested revision. Files that changed between either parent are
2046 1926 marked as changed for the next commit and a commit must be
2047 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 1953 def outgoing(ui, repo, dest=None, **opts):
2052 1954 """show changesets not found in destination
@@ -2058,7 +1960,7 b' def outgoing(ui, repo, dest=None, **opts'
2058 1960 See pull for valid destination format details.
2059 1961 """
2060 1962 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2061 ui.setconfig_remoteopts(**opts)
1963 setremoteconfig(ui, opts)
2062 1964 revs = None
2063 1965 if opts['rev']:
2064 1966 revs = [repo.lookup(rev) for rev in opts['rev']]
@@ -2079,16 +1981,31 b' def outgoing(ui, repo, dest=None, **opts'
2079 1981 displayer.show(changenode=n)
2080 1982 if opts['patch']:
2081 1983 prev = (parents and parents[0]) or nullid
2082 dodiff(ui, ui, repo, prev, n)
1984 patch.diff(repo, prev, n)
2083 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 1988 """show the parents of the working dir or revision
2087 1989
2088 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 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 2009 else:
2093 2010 p = repo.dirstate.parents()
2094 2011
@@ -2125,7 +2042,7 b' def postincoming(ui, repo, modheads, opt'
2125 2042 return
2126 2043 if optupdate:
2127 2044 if modheads == 1:
2128 return doupdate(ui, repo)
2045 return hg.update(repo, repo.changelog.tip()) # update
2129 2046 else:
2130 2047 ui.status(_("not updating, since new heads added\n"))
2131 2048 if modheads > 1:
@@ -2165,7 +2082,7 b' def pull(ui, repo, source="default", **o'
2165 2082 with the --ssh command line option.
2166 2083 """
2167 2084 source = ui.expandpath(source)
2168 ui.setconfig_remoteopts(**opts)
2085 setremoteconfig(ui, opts)
2169 2086
2170 2087 other = hg.repository(ui, source)
2171 2088 ui.status(_('pulling from %s\n') % (source))
@@ -2203,7 +2120,7 b' def push(ui, repo, dest=None, **opts):'
2203 2120 feature is enabled on the remote Mercurial server.
2204 2121 """
2205 2122 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2206 ui.setconfig_remoteopts(**opts)
2123 setremoteconfig(ui, opts)
2207 2124
2208 2125 other = hg.repository(ui, dest)
2209 2126 ui.status('pushing to %s\n' % (dest))
@@ -2257,7 +2174,7 b' def recover(ui, repo):'
2257 2174 operation. It should only be necessary when Mercurial suggests it.
2258 2175 """
2259 2176 if repo.recover():
2260 return repo.verify()
2177 return hg.verify(repo)
2261 2178 return 1
2262 2179
2263 2180 def remove(ui, repo, *pats, **opts):
@@ -2277,12 +2194,12 b' def remove(ui, repo, *pats, **opts):'
2277 2194 names = []
2278 2195 if not opts['after'] and not pats:
2279 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 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 2200 modified, added, removed, deleted, unknown = mardu
2284 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 2203 reason = None
2287 2204 if abs not in deleted and opts['after']:
2288 2205 reason = _('is still present')
@@ -2389,20 +2306,21 b' def revert(ui, repo, *pats, **opts):'
2389 2306
2390 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 2311 names[abs] = (rel, exact)
2394 2312 if src == 'b':
2395 2313 target_only[abs] = True
2396 2314
2397 2315 # walk target manifest.
2398 2316
2399 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2400 badmatch=names.has_key):
2317 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2318 badmatch=names.has_key):
2401 2319 if abs in names: continue
2402 2320 names[abs] = (rel, exact)
2403 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 2324 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2407 2325
2408 2326 revert = ([], _('reverting %s\n'))
@@ -2474,8 +2392,7 b' def revert(ui, repo, *pats, **opts):'
2474 2392
2475 2393 if not opts.get('dry_run'):
2476 2394 repo.dirstate.forget(forget[0])
2477 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2478 show_stats=False)
2395 r = hg.revert(repo, node, update.has_key, wlock)
2479 2396 repo.dirstate.update(add[0], 'a')
2480 2397 repo.dirstate.update(undelete[0], 'n')
2481 2398 repo.dirstate.update(remove[0], 'r')
@@ -2593,37 +2510,44 b' def serve(ui, repo, **opts):'
2593 2510 def status(ui, repo, *pats, **opts):
2594 2511 """show changed files in the working directory
2595 2512
2596 Show changed files in the repository. If names are
2597 given, only files that match are shown.
2513 Show status of files in the repository. If names are given, only
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 2517 The codes used to show the status of files are:
2600 2518 M = modified
2601 2519 A = added
2602 2520 R = removed
2521 C = clean
2603 2522 ! = deleted, but still tracked
2604 2523 ? = not tracked
2605 2524 I = ignored (not shown by default)
2606 2525 = the previous added file was copied from here
2607 2526 """
2608 2527
2609 show_ignored = opts['ignored'] and True or False
2610 files, matchfn, anypats = matchpats(repo, pats, opts)
2528 all = opts['all']
2529
2530 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2611 2531 cwd = (pats and repo.getcwd()) or ''
2612 modified, added, removed, deleted, unknown, ignored = [
2532 modified, added, removed, deleted, unknown, ignored, clean = [
2613 2533 [util.pathto(cwd, x) for x in n]
2614 for n in repo.changes(files=files, match=matchfn,
2615 show_ignored=show_ignored)]
2616
2617 changetypes = [('modified', 'M', modified),
2534 for n in repo.status(files=files, match=matchfn,
2535 list_ignored=all or opts['ignored'],
2536 list_clean=all or opts['clean'])]
2537
2538 changetypes = (('modified', 'M', modified),
2618 2539 ('added', 'A', added),
2619 2540 ('removed', 'R', removed),
2620 2541 ('deleted', '!', deleted),
2621 2542 ('unknown', '?', unknown),
2622 ('ignored', 'I', ignored)]
2543 ('ignored', 'I', ignored))
2544
2545 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2623 2546
2624 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 2551 or changetypes):
2628 2552 if opts['no_status']:
2629 2553 format = "%%s%s" % end
@@ -2632,7 +2556,7 b' def status(ui, repo, *pats, **opts):'
2632 2556
2633 2557 for f in changes:
2634 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 2560 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2637 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 2569 very useful to compare different revision, to go back to significant
2646 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 2574 To facilitate version control, distribution, and merging of tags,
2651 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 2577 necessary. The file '.hg/localtags' is used for local tags (not
2654 2578 shared among repositories).
2655 2579 """
2656 if name == "tip":
2657 raise util.Abort(_("the name 'tip' is reserved"))
2580 if name in ['tip', '.']:
2581 raise util.Abort(_("the name '%s' is reserved") % name)
2658 2582 if rev_ is not None:
2659 2583 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2660 2584 "please use 'hg tag [-r REV] NAME' instead\n"))
@@ -2665,7 +2589,12 b' def tag(ui, repo, name, rev_=None, **opt'
2665 2589 if rev_:
2666 2590 r = hex(repo.lookup(rev_))
2667 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 2599 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2671 2600 opts['date'])
@@ -2701,7 +2630,7 b' def tip(ui, repo, **opts):'
2701 2630 br = repo.branchlookup([n])
2702 2631 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2703 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 2635 def unbundle(ui, repo, fname, **opts):
2707 2636 """apply a changegroup file
@@ -2730,7 +2659,8 b' def unbundle(ui, repo, fname, **opts):'
2730 2659 raise util.Abort(_("%s: unknown bundle compression type")
2731 2660 % fname)
2732 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 2664 return postincoming(ui, repo, modheads, opts['update'])
2735 2665
2736 2666 def undo(ui, repo):
@@ -2745,7 +2675,7 b' def undo(ui, repo):'
2745 2675 repo.rollback()
2746 2676
2747 2677 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2748 branch=None, **opts):
2678 branch=None):
2749 2679 """update or merge working directory
2750 2680
2751 2681 Update the working directory to the specified revision.
@@ -2760,13 +2690,17 b' def update(ui, repo, node=None, merge=Fa'
2760 2690 By default, update will refuse to run if doing so would require
2761 2691 merging or discarding local changes.
2762 2692 """
2693 node = _lookup(repo, node, branch)
2763 2694 if merge:
2764 2695 ui.warn(_('(the -m/--merge option is deprecated; '
2765 2696 'use the merge command instead)\n'))
2766 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2767
2768 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2769 branch=None, **opts):
2697 return hg.merge(repo, node, force=force)
2698 elif clean:
2699 return hg.clean(repo, node)
2700 else:
2701 return hg.update(repo, node)
2702
2703 def _lookup(repo, node, branch=None):
2770 2704 if branch:
2771 2705 br = repo.branchlookup(branch=branch)
2772 2706 found = []
@@ -2774,19 +2708,19 b' def doupdate(ui, repo, node=None, merge='
2774 2708 if branch in br[x]:
2775 2709 found.append(x)
2776 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 2712 for x in found:
2779 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2780 return 1
2713 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2714 raise util.Abort("")
2781 2715 if len(found) == 1:
2782 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 2719 else:
2785 ui.warn(_("branch %s not found\n") % (branch))
2786 return 1
2720 raise util.Abort(_("branch %s not found\n") % (branch))
2787 2721 else:
2788 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 2725 def verify(ui, repo):
2792 2726 """verify the integrity of the repository
@@ -2798,7 +2732,7 b' def verify(ui, repo):'
2798 2732 the changelog, manifest, and tracked files, as well as the
2799 2733 integrity of their crosslinks and indices.
2800 2734 """
2801 return repo.verify()
2735 return hg.verify(repo)
2802 2736
2803 2737 # Command options and aliases are listed here, alphabetically
2804 2738
@@ -2919,6 +2853,7 b' table = {'
2919 2853 ('a', 'text', None, _('treat all files as text')),
2920 2854 ('p', 'show-function', None,
2921 2855 _('show which function each change is in')),
2856 ('g', 'git', None, _('use git extended diff format')),
2922 2857 ('w', 'ignore-all-space', None,
2923 2858 _('ignore white space when comparing lines')),
2924 2859 ('b', 'ignore-space-change', None,
@@ -2943,6 +2878,8 b' table = {'
2943 2878 (grep,
2944 2879 [('0', 'print0', None, _('end fields with NUL')),
2945 2880 ('', 'all', None, _('print all revisions that match')),
2881 ('f', 'follow', None,
2882 _('follow changeset history, or file history across copies and renames')),
2946 2883 ('i', 'ignore-case', None, _('ignore case when matching')),
2947 2884 ('l', 'files-with-matches', None,
2948 2885 _('print only filenames and revs that match')),
@@ -2979,7 +2916,7 b' table = {'
2979 2916 ('n', 'newest-first', None, _('show newest record first')),
2980 2917 ('', 'bundle', '', _('file to store the bundles into')),
2981 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 2920 ('', 'template', '', _('display with template')),
2984 2921 ('e', 'ssh', '', _('specify ssh command to use')),
2985 2922 ('', 'remotecmd', '',
@@ -3005,6 +2942,10 b' table = {'
3005 2942 "^log|history":
3006 2943 (log,
3007 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 2949 ('k', 'keyword', [], _('search for a keyword')),
3009 2950 ('l', 'limit', '', _('limit number of changes displayed')),
3010 2951 ('r', 'rev', [], _('show the specified revision or range')),
@@ -3012,6 +2953,7 b' table = {'
3012 2953 ('', 'style', '', _('display using template map file')),
3013 2954 ('m', 'only-merges', None, _('show only merges')),
3014 2955 ('p', 'patch', None, _('show patch')),
2956 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3015 2957 ('', 'template', '', _('display with template')),
3016 2958 ('I', 'include', [], _('include names matching the given patterns')),
3017 2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
@@ -3038,9 +2980,10 b' table = {'
3038 2980 "^parents":
3039 2981 (parents,
3040 2982 [('b', 'branches', None, _('show branches')),
2983 ('r', 'rev', '', _('show parents from the specified rev')),
3041 2984 ('', 'style', '', _('display using template map file')),
3042 2985 ('', 'template', '', _('display with template'))],
3043 _('hg parents [-b] [REV]')),
2986 _('hg parents [-b] [-r REV] [FILE]')),
3044 2987 "paths": (paths, [], _('hg paths [NAME]')),
3045 2988 "^pull":
3046 2989 (pull,
@@ -3049,7 +2992,7 b' table = {'
3049 2992 ('e', 'ssh', '', _('specify ssh command to use')),
3050 2993 ('f', 'force', None,
3051 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 2996 ('', 'remotecmd', '',
3054 2997 _('specify hg command to run on the remote side'))],
3055 2998 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
@@ -3117,10 +3060,12 b' table = {'
3117 3060 _('hg serve [OPTION]...')),
3118 3061 "^status|st":
3119 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 3065 ('a', 'added', None, _('show only added files')),
3122 3066 ('r', 'removed', None, _('show only removed files')),
3123 3067 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3068 ('c', 'clean', None, _('show only files without changes')),
3124 3069 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3125 3070 ('i', 'ignored', None, _('show ignored files')),
3126 3071 ('n', 'no-status', None, _('hide status prefix')),
@@ -3286,24 +3231,16 b' def findext(name):'
3286 3231 try:
3287 3232 return sys.modules[external[name]]
3288 3233 except KeyError:
3289 dotname = '.' + name
3290 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 3236 return sys.modules[v]
3293 3237 raise KeyError(name)
3294 3238
3295 def dispatch(args):
3296 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3297 num = getattr(signal, name, None)
3298 if num: signal.signal(num, catchterm)
3299
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():
3239 def load_extensions(ui):
3240 added = []
3241 for ext_name, load_from_name in ui.extensions():
3242 if ext_name in external:
3243 continue
3307 3244 try:
3308 3245 if load_from_name:
3309 3246 # the module will be loaded in sys.modules
@@ -3323,23 +3260,36 b' def dispatch(args):'
3323 3260 except ImportError:
3324 3261 mod = importh(ext_name)
3325 3262 external[ext_name] = mod.__name__
3263 added.append((mod, ext_name))
3326 3264 except (util.SignalInterrupt, KeyboardInterrupt):
3327 3265 raise
3328 3266 except Exception, inst:
3329 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3330 if u.print_exc():
3267 ui.warn(_("*** failed to import extension %s: %s\n") %
3268 (ext_name, inst))
3269 if ui.print_exc():
3331 3270 return 1
3332 3271
3333 for name in external.itervalues():
3334 mod = sys.modules[name]
3272 for mod, name in added:
3335 3273 uisetup = getattr(mod, 'uisetup', None)
3336 3274 if uisetup:
3337 uisetup(u)
3275 uisetup(ui)
3338 3276 cmdtable = getattr(mod, 'cmdtable', {})
3339 3277 for t in cmdtable:
3340 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 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 3294 try:
3345 3295 cmd, func, args, options, cmdoptions = parse(u, args)
@@ -3391,6 +3341,7 b' def dispatch(args):'
3391 3341 mod = sys.modules[name]
3392 3342 if hasattr(mod, 'reposetup'):
3393 3343 mod.reposetup(u, repo)
3344 hg.repo_setup_hooks.append(mod.reposetup)
3394 3345 except hg.RepoError:
3395 3346 if cmd not in optionalrepo.split():
3396 3347 raise
@@ -3398,6 +3349,11 b' def dispatch(args):'
3398 3349 else:
3399 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 3357 try:
3402 3358 if options['profile']:
3403 3359 import hotshot, hotshot.stats
@@ -1,6 +1,6 b''
1 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -11,9 +11,8 b' class changectx(object):'
11 11 def __init__(self, repo, changeid):
12 12 """changeid is a revision number, node, or tag"""
13 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 16 self._rev = self._repo.changelog.rev(self._node)
18 17
19 18 def changeset(self):
@@ -74,39 +73,40 b' class filectx(object):'
74 73 fileid can be a file revision or node."""
75 74 self._repo = repo
76 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 80 # if given a changeset id, go ahead and look up the file
82 self._changeset = self._repo.changelog.read(self._id)
83 node, flag = self._repo.manifest.find(self._changeset[0], path)
81 self._changeid = changeid
82 self._changectx = self.changectx()
84 83 self._filelog = self._repo.file(self._path)
85 self._filenode = node
86 elif self._fileid:
84 self._filenode = self._changectx.filenode(self._path)
85 else:
87 86 # else be lazy
88 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 90 self._filerev = self._filelog.rev(self._filenode)
91 91
92 def changeset(self):
92 def changectx(self):
93 93 try:
94 return self._changeset
94 return self._changectx
95 95 except AttributeError:
96 self._changeset = self._repo.changelog.read(self.node())
97 return self._changeset
96 self._changectx = changectx(self._repo, self._changeid)
97 return self._changectx
98 98
99 99 def filerev(self): return self._filerev
100 100 def filenode(self): return self._filenode
101 101 def filelog(self): return self._filelog
102 102
103 def rev(self): return self.changeset().rev()
104 def node(self): return self.changeset().node()
105 def user(self): return self.changeset().user()
106 def date(self): return self.changeset().date()
107 def files(self): return self.changeset().files()
108 def description(self): return self.changeset().description()
109 def manifest(self): return self.changeset().manifest()
103 def rev(self): return self.changectx().rev()
104 def node(self): return self.changectx().node()
105 def user(self): return self.changectx().user()
106 def date(self): return self.changectx().date()
107 def files(self): return self.changectx().files()
108 def description(self): return self.changectx().description()
109 def manifest(self): return self.changectx().manifest()
110 110
111 111 def data(self): return self._filelog.read(self._filenode)
112 112 def metadata(self): return self._filelog.readmeta(self._filenode)
@@ -96,6 +96,7 b' def demandload(scope, modules):'
96 96
97 97 foo import foo
98 98 foo bar import foo, bar
99 foo@bar import foo as bar
99 100 foo.bar import foo.bar
100 101 foo:bar from foo import bar
101 102 foo:bar,quux from foo import bar, quux
@@ -108,6 +109,9 b' def demandload(scope, modules):'
108 109 mod = mod[:col]
109 110 else:
110 111 fromlist = []
112 as = None
113 if '@' in mod:
114 mod, as = mod.split("@")
111 115 importer = _importer(scope, mod, fromlist)
112 116 if fromlist:
113 117 for name in fromlist:
@@ -126,4 +130,6 b' def demandload(scope, modules):'
126 130 continue
127 131 else:
128 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 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 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
@@ -10,7 +10,7 b' of the GNU General Public License, incor'
10 10 from node import *
11 11 from i18n import gettext as _
12 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 15 class dirstate(object):
16 16 format = ">cllll"
@@ -22,6 +22,7 b' class dirstate(object):'
22 22 self.ui = ui
23 23 self.map = None
24 24 self.pl = None
25 self.dirs = None
25 26 self.copies = {}
26 27 self.ignorefunc = None
27 28 self.blockignore = False
@@ -197,6 +198,38 b' class dirstate(object):'
197 198 def copied(self, file):
198 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 233 def update(self, files, state, **kw):
201 234 ''' current states:
202 235 n normal
@@ -207,10 +240,16 b' class dirstate(object):'
207 240 if not files: return
208 241 self.lazyread()
209 242 self.markdirty()
243 if state == "a":
244 self.initdirs()
245 self.checkshadows(files)
210 246 for f in files:
211 247 if state == "r":
212 248 self.map[f] = ('r', 0, 0, 0)
249 self.updatedirs(f, -1)
213 250 else:
251 if state == "a":
252 self.updatedirs(f, 1)
214 253 s = os.lstat(self.wjoin(f))
215 254 st_size = kw.get('st_size', s.st_size)
216 255 st_mtime = kw.get('st_mtime', s.st_mtime)
@@ -222,9 +261,11 b' class dirstate(object):'
222 261 if not files: return
223 262 self.lazyread()
224 263 self.markdirty()
264 self.initdirs()
225 265 for f in files:
226 266 try:
227 267 del self.map[f]
268 self.updatedirs(f, -1)
228 269 except KeyError:
229 270 self.ui.warn(_("not in dirstate: %s!\n") % f)
230 271 pass
@@ -232,14 +273,15 b' class dirstate(object):'
232 273 def clear(self):
233 274 self.map = {}
234 275 self.copies = {}
276 self.dirs = None
235 277 self.markdirty()
236 278
237 279 def rebuild(self, parent, files):
238 280 self.clear()
239 281 umask = os.umask(0)
240 282 os.umask(umask)
241 for f, mode in files:
242 if mode:
283 for f in files:
284 if files.execf(f):
243 285 self.map[f] = ('n', ~umask, -1, 0)
244 286 else:
245 287 self.map[f] = ('n', ~umask & 0666, -1, 0)
@@ -344,6 +386,10 b' class dirstate(object):'
344 386 # directly by this function, but might be modified by your statmatch call.
345 387 #
346 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 393 # recursion free walker, faster than os.walk.
348 394 def findfiles(s):
349 395 work = [s]
@@ -352,7 +398,7 b' class dirstate(object):'
352 398 names = os.listdir(top)
353 399 names.sort()
354 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 402 if nd == '.':
357 403 nd = ''
358 404 else:
@@ -434,15 +480,16 b' class dirstate(object):'
434 480 if not seen(k) and (statmatch(k, None)):
435 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 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 489 try:
443 490 type_, mode, size, time = self[fn]
444 491 except KeyError:
445 if show_ignored and self.ignore(fn):
492 if list_ignored and self.ignore(fn):
446 493 ignored.append(fn)
447 494 else:
448 495 unknown.append(fn)
@@ -473,6 +520,8 b' class dirstate(object):'
473 520 modified.append(fn)
474 521 elif time != st.st_mtime:
475 522 lookup.append(fn)
523 elif list_clean:
524 clean.append(fn)
476 525 elif type_ == 'm':
477 526 modified.append(fn)
478 527 elif type_ == 'a':
@@ -480,4 +529,5 b' class dirstate(object):'
480 529 elif type_ == 'r':
481 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 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -65,6 +65,26 b' class filelog(revlog):'
65 65 return (m["copy"], bin(m["copyrev"]))
66 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 88 def annotate(self, node):
69 89
70 90 def decorate(text, rev):
@@ -76,31 +96,59 b' class filelog(revlog):'
76 96 return child
77 97
78 98 # find all ancestors
79 needed = {node:1}
80 visit = [node]
99 needed = {(self, node):1}
100 files = [self]
101 visit = [(self, node)]
81 102 while visit:
82 n = visit.pop(0)
83 for p in self.parents(n):
84 if p not in needed:
85 needed[p] = 1
86 visit.append(p)
103 f, n = visit.pop(0)
104 rn = f.renamed(n)
105 if rn:
106 f, n = rn
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 119 else:
88 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
92 visit = [ (self.rev(n), n) for n in needed.keys() ]
93 visit.sort()
123 # sort by revision (per file) which is a topological order
124 visit = []
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 129 hist = {}
95 130
96 for r,n in visit:
97 curr = decorate(self.read(n), self.linkrev(n))
98 for p in self.parents(n):
131 for i in range(len(visit)):
132 r, f, n = visit[i]
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 147 if p != nullid:
100 148 curr = pair(hist[p], curr)
101 149 # trim the history of unneeded revs
102 needed[p] -= 1
103 if not needed[p]:
150 needed[(f, p)] -= 1
151 if not needed[(f, p)]:
104 152 del hist[p]
105 153 hist[n] = curr
106 154
@@ -1,6 +1,7 b''
1 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 6 # This software may be used and distributed according to the terms
6 7 # of the GNU General Public License, incorporated herein by reference.
@@ -10,69 +11,56 b' from repo import *'
10 11 from demandload import *
11 12 from i18n import gettext as _
12 13 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
13 demandload(globals(), "errno lock os shutil util")
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://"))
14 demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
30 15
31 def local_(ui, path, create=0):
32 if path.startswith('file:'):
33 path = path[5:]
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://"))
16 def _local(path):
17 return (os.path.isfile(path and util.drop_scheme('file', path)) and
18 bundlerepo or localrepo)
48 19
49 20 schemes = {
50 'bundle': bundle,
51 'file': local_,
52 'hg': hg,
53 'http': lambda ui, path: httprepo.httprepository(ui, path),
54 'https': lambda ui, path: httprepo.httpsrepository(ui, path),
55 'old-http': old_http,
56 'ssh': ssh_,
57 'static-http': static_http,
21 'bundle': bundlerepo,
22 'file': _local,
23 'hg': httprepo,
24 'http': httprepo,
25 'https': httprepo,
26 'old-http': statichttprepo,
27 'ssh': sshrepo,
28 'static-http': statichttprepo,
58 29 }
59 30
60 def repository(ui, path=None, create=0):
61 scheme = None
31 def _lookup(path):
32 scheme = 'file'
62 33 if path:
63 34 c = path.find(':')
64 35 if c > 0:
65 scheme = schemes.get(path[:c])
66 else:
67 path = ''
68 ctor = scheme or schemes['file']
69 if create:
36 scheme = path[:c]
37 thing = schemes.get(scheme) or schemes['file']
38 try:
39 return thing(path)
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 46 try:
71 return ctor(ui, path, create)
72 except TypeError:
73 raise util.Abort(_('cannot create new repository over "%s" protocol') %
74 scheme)
75 return ctor(ui, path)
47 return _lookup(repo).islocal(repo)
48 except AttributeError:
49 return False
50 return repo.local()
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 65 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
78 66 stream=False):
@@ -90,7 +78,9 b' def clone(ui, source, dest=None, pull=Fa'
90 78 If an exception is raised, the partly cloned/updated destination
91 79 repository will be deleted.
92 80
93 Keyword arguments:
81 Arguments:
82
83 source: repository object or URL
94 84
95 85 dest: URL of destination repository to create (defaults to base
96 86 name of source repository)
@@ -105,8 +95,24 b' def clone(ui, source, dest=None, pull=Fa'
105 95 update: update working directory after clone completes, if
106 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 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 117 if os.path.exists(dest):
112 118 raise util.Abort(_("destination '%s' already exists"), dest)
@@ -121,8 +127,6 b' def clone(ui, source, dest=None, pull=Fa'
121 127 if self.dir_:
122 128 self.rmtree(self.dir_, True)
123 129
124 src_repo = repository(ui, source)
125
126 130 dest_repo = None
127 131 try:
128 132 dest_repo = repository(ui, dest)
@@ -133,7 +137,7 b' def clone(ui, source, dest=None, pull=Fa'
133 137 dest_path = None
134 138 dir_cleanup = None
135 139 if dest_repo.local():
136 dest_path = os.path.realpath(dest)
140 dest_path = os.path.realpath(dest_repo.root)
137 141 dir_cleanup = DirCleanup(dest_path)
138 142
139 143 abspath = source
@@ -202,8 +206,31 b' def clone(ui, source, dest=None, pull=Fa'
202 206 dest_lock.release()
203 207
204 208 if update:
205 dest_repo.update(dest_repo.changelog.tip())
209 _merge.update(dest_repo, dest_repo.changelog.tip())
206 210 if dir_cleanup:
207 211 dir_cleanup.close()
208 212
209 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 1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
2 2 #
3 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 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 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 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
@@ -11,7 +11,7 b' import os.path'
11 11 import mimetypes
12 12 from mercurial.demandload import demandload
13 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 15 demandload(globals(), "mercurial:templater")
16 16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 17 from mercurial.node import *
@@ -37,6 +37,7 b' class hgweb(object):'
37 37 self.mtime = -1
38 38 self.reponame = name
39 39 self.archives = 'zip', 'gz', 'bz2'
40 self.stripecount = 1
40 41 self.templatepath = self.repo.ui.config("web", "templates",
41 42 templater.templatepath())
42 43
@@ -46,6 +47,8 b' class hgweb(object):'
46 47 self.mtime = mtime
47 48 self.repo = hg.repository(self.repo.ui, self.repo.root)
48 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 52 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
50 53 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
51 54
@@ -126,39 +129,29 b' class hgweb(object):'
126 129 date1 = util.datestr(change1[2])
127 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 133 if files:
131 134 modified, added, removed = map(lambda x: filterfiles(files, x),
132 135 (modified, added, removed))
133 136
134 diffopts = self.repo.ui.diffopts()
135 showfunc = diffopts['showfunc']
136 ignorews = diffopts['ignorews']
137 ignorewsamount = diffopts['ignorewsamount']
138 ignoreblanklines = diffopts['ignoreblanklines']
137 diffopts = patch.diffopts(self.repo.ui)
139 138 for f in modified:
140 139 to = r.file(f).read(mmap1[f])
141 140 tn = r.file(f).read(mmap2[f])
142 141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
143 showfunc=showfunc, ignorews=ignorews,
144 ignorewsamount=ignorewsamount,
145 ignoreblanklines=ignoreblanklines), f, tn)
142 opts=diffopts), f, tn)
146 143 for f in added:
147 144 to = None
148 145 tn = r.file(f).read(mmap2[f])
149 146 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
150 showfunc=showfunc, ignorews=ignorews,
151 ignorewsamount=ignorewsamount,
152 ignoreblanklines=ignoreblanklines), f, tn)
147 opts=diffopts), f, tn)
153 148 for f in removed:
154 149 to = r.file(f).read(mmap1[f])
155 150 tn = None
156 151 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
157 showfunc=showfunc, ignorews=ignorews,
158 ignorewsamount=ignorewsamount,
159 ignoreblanklines=ignoreblanklines), f, tn)
152 opts=diffopts), f, tn)
160 153
161 def changelog(self, pos):
154 def changelog(self, pos, shortlog=False):
162 155 def changenav(**map):
163 156 def seq(factor, maxchanges=None):
164 157 if maxchanges:
@@ -173,8 +166,9 b' class hgweb(object):'
173 166
174 167 l = []
175 168 last = 0
176 for f in seq(1, self.maxchanges):
177 if f < self.maxchanges or f <= last:
169 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
170 for f in seq(1, maxchanges):
171 if f < maxchanges or f <= last:
178 172 continue
179 173 if f > count:
180 174 break
@@ -219,14 +213,15 b' class hgweb(object):'
219 213 for e in l:
220 214 yield e
221 215
216 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
222 217 cl = self.repo.changelog
223 218 mf = cl.read(cl.tip())[0]
224 219 count = cl.count()
225 start = max(0, pos - self.maxchanges + 1)
226 end = min(count, start + self.maxchanges)
220 start = max(0, pos - maxchanges + 1)
221 end = min(count, start + maxchanges)
227 222 pos = end - 1
228 223
229 yield self.t('changelog',
224 yield self.t(shortlog and 'shortlog' or 'changelog',
230 225 changenav=changenav,
231 226 manifest=hex(mf),
232 227 rev=pos, changesets=count, entries=changelist,
@@ -265,7 +260,7 b' class hgweb(object):'
265 260 hn = hex(n)
266 261
267 262 yield self.t('searchentry',
268 parity=count & 1,
263 parity=self.stripes(count),
269 264 author=changes[1],
270 265 parent=self.siblings(cl.parents(n), cl.rev),
271 266 child=self.siblings(cl.children(n), cl.rev),
@@ -376,7 +371,7 b' class hgweb(object):'
376 371 for l, t in enumerate(text.splitlines(1)):
377 372 yield {"line": t,
378 373 "linenumber": "% 6d" % (l + 1),
379 "parity": l & 1}
374 "parity": self.stripes(l)}
380 375
381 376 yield self.t("filerevision",
382 377 file=f,
@@ -393,7 +388,7 b' class hgweb(object):'
393 388 parent=self.siblings(fl.parents(n), fl.rev, file=f),
394 389 child=self.siblings(fl.children(n), fl.rev, file=f),
395 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 393 def fileannotate(self, f, node):
399 394 bcache = {}
@@ -409,7 +404,7 b' class hgweb(object):'
409 404 mfn = cs[0]
410 405
411 406 def annotate(**map):
412 parity = 1
407 parity = 0
413 408 last = None
414 409 for r, l in fl.annotate(n):
415 410 try:
@@ -447,7 +442,7 b' class hgweb(object):'
447 442 rename=self.renamelink(fl, n),
448 443 parent=self.siblings(fl.parents(n), fl.rev, file=f),
449 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 447 def manifest(self, mnode, path):
453 448 man = self.repo.manifest
@@ -457,7 +452,6 b' class hgweb(object):'
457 452 rev = man.rev(mn)
458 453 changerev = man.linkrev(mn)
459 454 node = self.repo.changelog.node(changerev)
460 mff = man.readflags(mn)
461 455
462 456 files = {}
463 457
@@ -489,10 +483,10 b' class hgweb(object):'
489 483 yield {"file": full,
490 484 "manifest": mnode,
491 485 "filenode": hex(fnode),
492 "parity": parity,
486 "parity": self.stripes(parity),
493 487 "basename": f,
494 "permissions": mff[full]}
495 parity = 1 - parity
488 "permissions": mf.execf(full)}
489 parity += 1
496 490
497 491 def dirlist(**map):
498 492 parity = 0
@@ -503,11 +497,11 b' class hgweb(object):'
503 497 if fnode:
504 498 continue
505 499
506 yield {"parity": parity,
500 yield {"parity": self.stripes(parity),
507 501 "path": os.path.join(path, f),
508 502 "manifest": mnode,
509 503 "basename": f[:-1]}
510 parity = 1 - parity
504 parity += 1
511 505
512 506 yield self.t("manifest",
513 507 manifest=mnode,
@@ -530,12 +524,12 b' class hgweb(object):'
530 524 parity = 0
531 525 for k,n in i:
532 526 if notip and k == "tip": continue
533 yield {"parity": parity,
527 yield {"parity": self.stripes(parity),
534 528 "tag": k,
535 529 "tagmanifest": hex(cl.read(n)[0]),
536 530 "date": cl.read(n)[2],
537 531 "node": hex(n)}
538 parity = 1 - parity
532 parity += 1
539 533
540 534 yield self.t("tags",
541 535 manifest=hex(mf),
@@ -565,12 +559,12 b' class hgweb(object):'
565 559 t = c[2]
566 560
567 561 yield self.t("tagentry",
568 parity = parity,
562 parity = self.stripes(parity),
569 563 tag = k,
570 564 node = hex(n),
571 565 date = t,
572 566 tagmanifest = hex(m))
573 parity = 1 - parity
567 parity += 1
574 568
575 569 def changelist(**map):
576 570 parity = 0
@@ -609,7 +603,8 b' class hgweb(object):'
609 603 lastchange = (0, 0), # FIXME
610 604 manifest = hex(mf),
611 605 tags = tagentries,
612 shortlog = changelist)
606 shortlog = changelist,
607 archives=self.archivelist("tip"))
613 608
614 609 def filediff(self, file, changeset):
615 610 cl = self.repo.changelog
@@ -689,6 +684,7 b' class hgweb(object):'
689 684 def expand_form(form):
690 685 shortcuts = {
691 686 'cl': [('cmd', ['changelog']), ('rev', None)],
687 'sl': [('cmd', ['shortlog']), ('rev', None)],
692 688 'cs': [('cmd', ['changeset']), ('node', None)],
693 689 'f': [('cmd', ['file']), ('filenode', None)],
694 690 'fl': [('cmd', ['filelog']), ('filenode', None)],
@@ -752,6 +748,13 b' class hgweb(object):'
752 748 else:
753 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 758 def do_changelog(self, req):
756 759 hi = self.repo.changelog.count() - 1
757 760 if req.form.has_key('rev'):
@@ -764,6 +767,18 b' class hgweb(object):'
764 767
765 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 782 def do_changeset(self, req):
768 783 req.write(self.changeset(req.form['node'][0]))
769 784
@@ -895,9 +910,13 b' class hgweb(object):'
895 910 # require ssl by default, auth info cannot be sniffed and
896 911 # replayed
897 912 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
898 if ssl_req and not req.env.get('HTTPS'):
899 bail(_('ssl required\n'))
900 return
913 if ssl_req:
914 if not req.env.get('HTTPS'):
915 bail(_('ssl required\n'))
916 return
917 proto = 'https'
918 else:
919 proto = 'http'
901 920
902 921 # do not allow push unless explicitly allowed
903 922 if not self.check_perm(req, 'push', False):
@@ -943,7 +962,9 b' class hgweb(object):'
943 962 sys.stdout = cStringIO.StringIO()
944 963
945 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 968 finally:
948 969 val = sys.stdout.getvalue()
949 970 sys.stdout = old_stdout
@@ -1,7 +1,7 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 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 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 1 # hgweb/request.py - An http request from either CGI or the standalone server.
2 2 #
3 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 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 1 # hgweb/server.py - The standalone hg web server.
2 2 #
3 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 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
@@ -198,6 +198,7 b' def create_server(ui, repo):'
198 198 self.webdirmaker = hgwebdir
199 199 self.repoviewmaker = hgweb
200 200 self.reqmaker = wsgiapplication(self.make_handler)
201 self.daemon_threads = True
201 202
202 203 def make_handler(self):
203 204 if self.webdir_conf:
@@ -1,6 +1,6 b''
1 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -1,6 +1,7 b''
1 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 6 # This software may be used and distributed according to the terms
6 7 # of the GNU General Public License, incorporated herein by reference.
@@ -115,6 +116,7 b' else:'
115 116
116 117 class httprepository(remoterepository):
117 118 def __init__(self, ui, path):
119 self.path = path
118 120 self.caps = None
119 121 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
120 122 if query or frag:
@@ -124,8 +126,8 b' class httprepository(remoterepository):'
124 126 host, port, user, passwd = netlocsplit(netloc)
125 127
126 128 # urllib cannot handle URLs with embedded user or passwd
127 self.url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
128 urlpath, '', ''))
129 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
130 urlpath, '', ''))
129 131 self.ui = ui
130 132
131 133 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
@@ -189,6 +191,9 b' class httprepository(remoterepository):'
189 191 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
190 192 urllib2.install_opener(opener)
191 193
194 def url(self):
195 return self.path
196
192 197 # look up capabilities only when needed
193 198
194 199 def get_caps(self):
@@ -213,7 +218,7 b' class httprepository(remoterepository):'
213 218 q = {"cmd": cmd}
214 219 q.update(args)
215 220 qs = urllib.urlencode(q)
216 cu = "%s?%s" % (self.url, qs)
221 cu = "%s?%s" % (self._url, qs)
217 222 try:
218 223 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
219 224 except urllib2.HTTPError, inst:
@@ -234,13 +239,13 b' class httprepository(remoterepository):'
234 239 not proto.startswith('text/plain') and \
235 240 not proto.startswith('application/hg-changegroup'):
236 241 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
237 self.url)
242 self._url)
238 243
239 244 if proto.startswith('application/mercurial'):
240 245 version = proto[22:]
241 246 if float(version) > 0.1:
242 247 raise hg.RepoError(_("'%s' uses newer protocol %s") %
243 (self.url, version))
248 (self._url, version))
244 249
245 250 return resp
246 251
@@ -335,3 +340,13 b' class httpsrepository(httprepository):'
335 340 raise util.Abort(_('Python support for SSL and HTTPS '
336 341 'is not installed'))
337 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 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 6 This software may be used and distributed according to the terms
7 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 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -83,6 +83,9 b' class localrepository(repo.repository):'
83 83
84 84 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
85 85
86 def url(self):
87 return 'file:' + self.root
88
86 89 def hook(self, name, throw=False, **args):
87 90 def callhook(hname, funcname):
88 91 '''call python hook. hook is callable object, looked up as
@@ -195,7 +198,7 b' class localrepository(repo.repository):'
195 198 self.hook('tag', node=node, tag=name, local=local)
196 199 return
197 200
198 for x in self.changes():
201 for x in self.status()[:5]:
199 202 if '.hgtags' in x:
200 203 raise util.Abort(_('working copy of .hgtags is changed '
201 204 '(please commit .hgtags manually)'))
@@ -289,6 +292,10 b' class localrepository(repo.repository):'
289 292 try:
290 293 return self.tags()[key]
291 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 299 try:
293 300 return self.changelog.lookup(key)
294 301 except:
@@ -463,8 +470,7 b' class localrepository(repo.repository):'
463 470 p2 = p2 or self.dirstate.parents()[1] or nullid
464 471 c1 = self.changelog.read(p1)
465 472 c2 = self.changelog.read(p2)
466 m1 = self.manifest.read(c1[0])
467 mf1 = self.manifest.readflags(c1[0])
473 m1 = self.manifest.read(c1[0]).copy()
468 474 m2 = self.manifest.read(c2[0])
469 475 changed = []
470 476
@@ -477,36 +483,32 b' class localrepository(repo.repository):'
477 483 wlock = self.wlock()
478 484 l = self.lock()
479 485 tr = self.transaction()
480 mm = m1.copy()
481 mfm = mf1.copy()
482 486 linkrev = self.changelog.count()
483 487 for f in files:
484 488 try:
485 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 491 r = self.file(f)
488 mfm[f] = tm
489 492
490 493 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
491 494 if entry:
492 mm[f] = entry
495 m1[f] = entry
493 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 499 changed.append(f)
497 500 if update_dirstate:
498 501 self.dirstate.update([f], "n")
499 502 except IOError:
500 503 try:
501 del mm[f]
502 del mfm[f]
504 del m1[f]
503 505 if update_dirstate:
504 506 self.dirstate.forget([f])
505 507 except:
506 508 # deleted from p2?
507 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 512 user = user or self.ui.username()
511 513 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
512 514 tr.close()
@@ -530,15 +532,14 b' class localrepository(repo.repository):'
530 532 else:
531 533 self.ui.warn(_("%s not tracked!\n") % f)
532 534 else:
533 modified, added, removed, deleted, unknown = self.changes(match=match)
535 modified, added, removed, deleted, unknown = self.status(match=match)[:5]
534 536 commit = modified + added
535 537 remove = removed
536 538
537 539 p1, p2 = self.dirstate.parents()
538 540 c1 = self.changelog.read(p1)
539 541 c2 = self.changelog.read(p2)
540 m1 = self.manifest.read(c1[0])
541 mf1 = self.manifest.readflags(c1[0])
542 m1 = self.manifest.read(c1[0]).copy()
542 543 m2 = self.manifest.read(c2[0])
543 544
544 545 if not commit and not remove and not force and p2 == nullid:
@@ -564,7 +565,7 b' class localrepository(repo.repository):'
564 565 for f in commit:
565 566 self.ui.note(f + "\n")
566 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 569 t = self.wread(f)
569 570 except IOError:
570 571 self.ui.warn(_("trouble committing %s!\n") % f)
@@ -591,12 +592,11 b' class localrepository(repo.repository):'
591 592 changed.append(f)
592 593
593 594 # update manifest
594 m1 = m1.copy()
595 595 m1.update(new)
596 596 for f in remove:
597 597 if f in m1:
598 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 600 (new, remove))
601 601
602 602 # add changeset
@@ -658,9 +658,9 b' class localrepository(repo.repository):'
658 658 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
659 659 yield src, fn
660 660
661 def changes(self, node1=None, node2=None, files=[], match=util.always,
662 wlock=None, show_ignored=None):
663 """return changes between two nodes or node and working directory
661 def status(self, node1=None, node2=None, files=[], match=util.always,
662 wlock=None, list_ignored=False, list_clean=False):
663 """return status of files between two nodes or node and working directory
664 664
665 665 If node1 is None, use the first dirstate parent instead.
666 666 If node2 is None, compare node1 with working directory.
@@ -668,8 +668,7 b' class localrepository(repo.repository):'
668 668
669 669 def fcmp(fn, mf):
670 670 t1 = self.wread(fn)
671 t2 = self.file(fn).read(mf.get(fn, nullid))
672 return cmp(t1, t2)
671 return self.file(fn).cmp(mf.get(fn, nullid), t1)
673 672
674 673 def mfmatches(node):
675 674 change = self.changelog.read(node)
@@ -679,7 +678,9 b' class localrepository(repo.repository):'
679 678 del mf[fn]
680 679 return mf
681 680
682 modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[]
681 modified, added, removed, deleted, unknown = [], [], [], [], []
682 ignored, clean = [], []
683
683 684 compareworking = False
684 685 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
685 686 compareworking = True
@@ -697,8 +698,9 b' class localrepository(repo.repository):'
697 698 wlock = self.wlock(wait=0)
698 699 except lock.LockException:
699 700 wlock = None
700 lookup, modified, added, removed, deleted, unknown, ignored = (
701 self.dirstate.changes(files, match, show_ignored))
701 (lookup, modified, added, removed, deleted, unknown,
702 ignored, clean) = self.dirstate.status(files, match,
703 list_ignored, list_clean)
702 704
703 705 # are we comparing working dir against its parent?
704 706 if compareworking:
@@ -721,12 +723,11 b' class localrepository(repo.repository):'
721 723 del mf2[f]
722 724 else:
723 725 # we are comparing two revisions
724 deleted, unknown, ignored = [], [], []
725 726 mf2 = mfmatches(node2)
726 727
727 728 if not compareworking:
728 729 # flush lists from dirstate before comparing manifests
729 modified, added = [], []
730 modified, added, clean = [], [], []
730 731
731 732 # make sure to sort the files so we talk to the disk in a
732 733 # reasonable order
@@ -736,6 +737,8 b' class localrepository(repo.repository):'
736 737 if mf1.has_key(fn):
737 738 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
738 739 modified.append(fn)
740 elif list_clean:
741 clean.append(fn)
739 742 del mf1[fn]
740 743 else:
741 744 added.append(fn)
@@ -743,12 +746,9 b' class localrepository(repo.repository):'
743 746 removed = mf1.keys()
744 747
745 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 750 l.sort()
748 if show_ignored is None:
749 return (modified, added, removed, deleted, unknown)
750 else:
751 return (modified, added, removed, deleted, unknown, ignored)
751 return (modified, added, removed, deleted, unknown, ignored, clean)
752 752
753 753 def add(self, list, wlock=None):
754 754 if not wlock:
@@ -798,7 +798,6 b' class localrepository(repo.repository):'
798 798 def undelete(self, list, wlock=None):
799 799 p = self.dirstate.parents()[0]
800 800 mn = self.changelog.read(p)[0]
801 mf = self.manifest.readflags(mn)
802 801 m = self.manifest.read(mn)
803 802 if not wlock:
804 803 wlock = self.wlock()
@@ -808,7 +807,7 b' class localrepository(repo.repository):'
808 807 else:
809 808 t = self.file(f).read(m[f])
810 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 811 self.dirstate.update([f], "n")
813 812
814 813 def copy(self, source, dest, wlock=None):
@@ -1159,22 +1158,29 b' class localrepository(repo.repository):'
1159 1158 else:
1160 1159 return subset
1161 1160
1162 def pull(self, remote, heads=None, force=False):
1163 l = self.lock()
1161 def pull(self, remote, heads=None, force=False, lock=None):
1162 mylock = False
1163 if not lock:
1164 lock = self.lock()
1165 mylock = True
1164 1166
1165 fetch = self.findincoming(remote, force=force)
1166 if fetch == [nullid]:
1167 self.ui.status(_("requesting all changes\n"))
1167 try:
1168 fetch = self.findincoming(remote, force=force)
1169 if fetch == [nullid]:
1170 self.ui.status(_("requesting all changes\n"))
1168 1171
1169 if not fetch:
1170 self.ui.status(_("no changes found\n"))
1171 return 0
1172 if not fetch:
1173 self.ui.status(_("no changes found\n"))
1174 return 0
1172 1175
1173 if heads is None:
1174 cg = remote.changegroup(fetch, 'pull')
1175 else:
1176 cg = remote.changegroupsubset(fetch, heads, 'pull')
1177 return self.addchangegroup(cg, 'pull')
1176 if heads is None:
1177 cg = remote.changegroup(fetch, 'pull')
1178 else:
1179 cg = remote.changegroupsubset(fetch, heads, 'pull')
1180 return self.addchangegroup(cg, 'pull', remote.url())
1181 finally:
1182 if mylock:
1183 lock.release()
1178 1184
1179 1185 def push(self, remote, force=False, revs=None):
1180 1186 # there are two ways to push to remote repo:
@@ -1230,7 +1236,7 b' class localrepository(repo.repository):'
1230 1236 ret = self.prepush(remote, force, revs)
1231 1237 if ret[0] is not None:
1232 1238 cg, remote_heads = ret
1233 return remote.addchangegroup(cg, 'push')
1239 return remote.addchangegroup(cg, 'push', self.url())
1234 1240 return ret[1]
1235 1241
1236 1242 def push_unbundle(self, remote, force, revs):
@@ -1583,7 +1589,7 b' class localrepository(repo.repository):'
1583 1589
1584 1590 return util.chunkbuffer(gengroup())
1585 1591
1586 def addchangegroup(self, source, srctype):
1592 def addchangegroup(self, source, srctype, url):
1587 1593 """add changegroup to repo.
1588 1594 returns number of heads modified or added + 1."""
1589 1595
@@ -1597,7 +1603,7 b' class localrepository(repo.repository):'
1597 1603 if not source:
1598 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 1608 changesets = files = revisions = 0
1603 1609
@@ -1664,544 +1670,21 b' class localrepository(repo.repository):'
1664 1670
1665 1671 if changesets > 0:
1666 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 1676 tr.close()
1670 1677
1671 1678 if changesets > 0:
1672 1679 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1673 source=srctype)
1680 source=srctype, url=url)
1674 1681
1675 1682 for i in range(cor + 1, cnr + 1):
1676 1683 self.hook("incoming", node=hex(self.changelog.node(i)),
1677 source=srctype)
1684 source=srctype, url=url)
1678 1685
1679 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 1689 def stream_in(self, remote):
2207 1690 fp = remote.stream_out()
@@ -2227,7 +1710,7 b' class localrepository(repo.repository):'
2227 1710 util.bytecount(total_bytes / elapsed)))
2228 1711 self.reload()
2229 1712 return len(self.heads()) + 1
2230
1713
2231 1714 def clone(self, remote, heads=[], stream=False):
2232 1715 '''clone remote repository.
2233 1716
@@ -2256,3 +1739,8 b' def aftertrans(base):'
2256 1739 os.path.join(p, "undo.dirstate"))
2257 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 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -1,6 +1,6 b''
1 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -10,6 +10,31 b' from i18n import gettext as _'
10 10 from demandload import *
11 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 38 class manifest(revlog):
14 39 def __init__(self, opener, defversion=REVLOGV0):
15 40 self.mapcache = None
@@ -18,26 +43,18 b' class manifest(revlog):'
18 43 defversion)
19 44
20 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 47 if self.mapcache and self.mapcache[0] == node:
23 48 return self.mapcache[1]
24 49 text = self.revision(node)
25 map = {}
26 flag = {}
27 50 self.listcache = array.array('c', text)
28 51 lines = text.splitlines(1)
52 mapping = manifestdict()
29 53 for l in lines:
30 54 (f, n) = l.split('\0')
31 map[f] = bin(n[:40])
32 flag[f] = (n[40:-1] == "x")
33 self.mapcache = (node, map, flag)
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]
55 mapping.rawset(f, n)
56 self.mapcache = (node, mapping)
57 return mapping
41 58
42 59 def diff(self, a, b):
43 60 return mdiff.textdiff(str(a), str(b))
@@ -86,7 +103,7 b' class manifest(revlog):'
86 103 '''look up entry for a single file efficiently.
87 104 return (node, flag) pair if found, (None, None) if not.'''
88 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 107 text = self.revision(node)
91 108 start, end = self._search(text, f)
92 109 if start == end:
@@ -95,7 +112,7 b' class manifest(revlog):'
95 112 f, n = l.split('\0')
96 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 116 changed=None):
100 117 # apply the changes collected during the bisect loop to our addlist
101 118 # return a delta suitable for addrevision
@@ -123,9 +140,7 b' class manifest(revlog):'
123 140
124 141 # if this is changed to support newlines in filenames,
125 142 # be sure to check the templates/ dir again (especially *-raw.tmpl)
126 text = ["%s\000%s%s\n" %
127 (f, hex(map[f]), flags[f] and "x" or '')
128 for f in files]
143 text = ["%s\000%s%s\n" % (f, hex(map[f]), map.flags(f)) for f in files]
129 144 self.listcache = array.array('c', "".join(text))
130 145 cachedelta = None
131 146 else:
@@ -151,8 +166,7 b' class manifest(revlog):'
151 166 # bs will either be the index of the item or the insert point
152 167 start, end = self._search(addbuf, f, start)
153 168 if w[1] == 0:
154 l = "%s\000%s%s\n" % (f, hex(map[f]),
155 flags[f] and "x" or '')
169 l = "%s\000%s%s\n" % (f, hex(map[f]), map.flags(f))
156 170 else:
157 171 l = ""
158 172 if start == end and w[1] == 1:
@@ -183,6 +197,6 b' class manifest(revlog):'
183 197
184 198 n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
185 199 p2, cachedelta)
186 self.mapcache = (n, map, flags)
200 self.mapcache = (n, map)
187 201
188 202 return n
@@ -1,6 +1,6 b''
1 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -19,14 +19,41 b' def splitnewlines(text):'
19 19 lines[-1] = lines[-1][:-1]
20 20 return lines
21 21
22 def unidiff(a, ad, b, bd, fn, r=None, text=False,
23 showfunc=False, ignorews=False, ignorewsamount=False,
24 ignoreblanklines=False):
22 class diffopts(object):
23 '''context is the number of context lines
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 53 if not a and not b: return ""
27 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 57 l = ['Binary file %s has changed\n' % fn]
31 58 elif not a:
32 59 b = splitnewlines(b)
@@ -49,10 +76,7 b' def unidiff(a, ad, b, bd, fn, r=None, te'
49 76 else:
50 77 al = splitnewlines(a)
51 78 bl = splitnewlines(b)
52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
53 showfunc=showfunc, ignorews=ignorews,
54 ignorewsamount=ignorewsamount,
55 ignoreblanklines=ignoreblanklines))
79 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, opts=opts))
56 80 if not l: return ""
57 81 # difflib uses a space, rather than a tab
58 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 96 # t1 and t2 are the text to be diffed
73 97 # l1 and l2 are the text broken up into lines
74 98 # header1 and header2 are the filenames for the diff output
75 # context is the number of context lines
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):
99 def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
82 100 def contextend(l, len):
83 ret = l + context
101 ret = l + opts.context
84 102 if ret > len:
85 103 ret = len
86 104 return ret
87 105
88 106 def contextstart(l):
89 ret = l - context
107 ret = l - opts.context
90 108 if ret < 0:
91 109 return 0
92 110 return ret
@@ -101,7 +119,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
101 119 blen = b2 - bstart + aend - a2
102 120
103 121 func = ""
104 if showfunc:
122 if opts.showfunc:
105 123 # walk backwards from the start of the context
106 124 # to find a line starting with an alphanumeric char.
107 125 for x in xrange(astart, -1, -1):
@@ -119,14 +137,14 b' def bunidiff(t1, t2, l1, l2, header1, he'
119 137
120 138 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
121 139
122 if showfunc:
140 if opts.showfunc:
123 141 funcre = re.compile('\w')
124 if ignorewsamount:
142 if opts.ignorewsamount:
125 143 wsamountre = re.compile('[ \t]+')
126 144 wsappendedre = re.compile(' \n')
127 if ignoreblanklines:
145 if opts.ignoreblanklines:
128 146 wsblanklinesre = re.compile('\n')
129 if ignorews:
147 if opts.ignorews:
130 148 wsre = re.compile('[ \t]')
131 149
132 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 177 if not old and not new:
160 178 continue
161 179
162 if ignoreblanklines:
180 if opts.ignoreblanklines:
163 181 wsold = wsblanklinesre.sub('', "".join(old))
164 182 wsnew = wsblanklinesre.sub('', "".join(new))
165 183 if wsold == wsnew:
166 184 continue
167 185
168 if ignorewsamount:
186 if opts.ignorewsamount:
169 187 wsold = wsamountre.sub(' ', "".join(old))
170 188 wsold = wsappendedre.sub('\n', wsold)
171 189 wsnew = wsamountre.sub(' ', "".join(new))
@@ -173,7 +191,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
173 191 if wsold == wsnew:
174 192 continue
175 193
176 if ignorews:
194 if opts.ignorews:
177 195 wsold = wsre.sub('', "".join(old))
178 196 wsnew = wsre.sub('', "".join(new))
179 197 if wsold == wsnew:
@@ -184,7 +202,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
184 202 prev = None
185 203 if hunk:
186 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 206 prev = hunk
189 207 astart = hunk[1]
190 208 bstart = hunk[3]
@@ -14,7 +14,7 b''
14 14 allocation of intermediate Python objects. Working memory is about 2x
15 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 19 This software may be used and distributed according to the terms
20 20 of the GNU General Public License, incorporated herein by reference.
@@ -1,7 +1,7 b''
1 1 """
2 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 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
@@ -2,7 +2,7 b''
2 2 # Used for the py2exe distutil.
3 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 7 # This software may be used and distributed according to the terms
8 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
@@ -1,6 +1,7 b''
1 1 # repo.py - repository base classes for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 5 #
5 6 # This software may be used and distributed according to the terms
6 7 # of the GNU General Public License, incorporated herein by reference.
@@ -4,7 +4,7 b' revlog.py - storage back-end for mercuri'
4 4 This provides efficient delta storage with O(1) retrieve and append
5 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 9 This software may be used and distributed according to the terms
10 10 of the GNU General Public License, incorporated herein by reference.
@@ -469,7 +469,8 b' class revlog(object):'
469 469 return self.nodemap[node]
470 470 except KeyError:
471 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 474 def parents(self, node):
474 475 if node == nullid: return (nullid, nullid)
475 476 r = self.rev(node)
@@ -743,13 +744,8 b' class revlog(object):'
743 744
744 745 def lookup(self, id):
745 746 """locate a node based on revision number or subset of hex nodeid"""
746 if id in self.nodemap:
747 return id
748 747 if type(id) == type(0):
749 rev = id
750 if rev < 0: rev = self.count() + rev
751 if rev < 0 or rev >= self.count(): return None
752 return self.node(rev)
748 return self.node(id)
753 749 try:
754 750 rev = int(id)
755 751 if str(rev) != id: raise ValueError
@@ -762,10 +758,26 b' class revlog(object):'
762 758 if hex(n).startswith(id):
763 759 c.append(n)
764 760 if len(c) > 1: raise RevlogError(_("Ambiguous identifier"))
765 if len(c) < 1: raise RevlogError(_("No match found"))
766 return c[0]
761 if len(c) == 1: 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 782 def diff(self, a, b):
771 783 """return a delta between two revisions"""
@@ -1,6 +1,6 b''
1 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 5 # This software may be used and distributed according to the terms
6 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 14 class sshrepository(remoterepository):
15 15 def __init__(self, ui, path, create=0):
16 self.url = path
16 self._url = path
17 17 self.ui = ui
18 18
19 19 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
@@ -48,6 +48,9 b' class sshrepository(remoterepository):'
48 48
49 49 self.validate_repo(ui, sshcmd, args, remotecmd)
50 50
51 def url(self):
52 return self._url
53
51 54 def validate_repo(self, ui, sshcmd, args, remotecmd):
52 55 cmd = '%s %s "%s -R %s serve --stdio"'
53 56 cmd = cmd % (sshcmd, args, remotecmd, self.path)
@@ -180,7 +183,7 b' class sshrepository(remoterepository):'
180 183 return 1
181 184 return int(r)
182 185
183 def addchangegroup(self, cg, source):
186 def addchangegroup(self, cg, source, url):
184 187 d = self.call("addchangegroup")
185 188 if d:
186 189 raise hg.RepoError(_("push refused: %s") % d)
@@ -201,3 +204,5 b' class sshrepository(remoterepository):'
201 204
202 205 def stream_out(self):
203 206 return self.do_cmd('stream_out')
207
208 instance = sshrepository
@@ -1,6 +1,7 b''
1 1 # sshserver.py - ssh protocol server support for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 5 #
5 6 # This software may be used and distributed according to the terms
6 7 # of the GNU General Public License, incorporated herein by reference.
@@ -117,9 +118,13 b' class sshserver(object):'
117 118 return
118 119
119 120 self.respond("")
120 r = self.repo.addchangegroup(self.fin, 'serve')
121 r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
121 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 128 def do_unbundle(self):
124 129 their_heads = self.getarg()[1].split()
125 130
@@ -159,7 +164,7 b' class sshserver(object):'
159 164 # push can proceed
160 165
161 166 fp.seek(0)
162 r = self.repo.addchangegroup(fp, 'serve')
167 r = self.repo.addchangegroup(fp, 'serve', self.client_url())
163 168 self.respond(str(r))
164 169 finally:
165 170 if not was_locked:
@@ -2,14 +2,15 b''
2 2 #
3 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 7 # This software may be used and distributed according to the terms
8 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 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 15 class rangereader(httprangereader.httprangereader):
15 16 def read(self, size=None):
@@ -30,6 +31,7 b' def opener(base):'
30 31
31 32 class statichttprepository(localrepo.localrepository):
32 33 def __init__(self, ui, path):
34 self._url = path
33 35 self.path = (path + "/.hg")
34 36 self.ui = ui
35 37 self.revlogversion = 0
@@ -41,8 +43,22 b' class statichttprepository(localrepo.loc'
41 43 self.encodepats = None
42 44 self.decodepats = None
43 45
46 def url(self):
47 return 'static-' + self._url
48
44 49 def dev(self):
45 50 return -1
46 51
47 52 def local(self):
48 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 241 return text.replace('\n', '<br/>\n')
242 242
243 243 def obfuscate(text):
244 text = unicode(text, 'utf-8', 'replace')
244 245 return ''.join(['&#%d;' % ord(c) for c in text])
245 246
246 247 def domain(author):
@@ -458,7 +459,7 b' class changeset_templater(object):'
458 459 yield x
459 460
460 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 463 def showfiles(**args):
463 464 for x in showlist('file', files[0], **args): yield x
464 465 def showadds(**args):
@@ -6,7 +6,7 b''
6 6 # effectively log-structured, this should amount to simply truncating
7 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 11 # This software may be used and distributed according to the terms
12 12 # of the GNU General Public License, incorporated herein by reference.
@@ -1,22 +1,24 b''
1 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 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import gettext as _
9 9 from demandload import *
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
11 demandload(globals(), "ConfigParser templater traceback util")
10 demandload(globals(), "errno getpass os re socket sys tempfile")
11 demandload(globals(), "ConfigParser mdiff templater traceback util")
12 12
13 13 class ui(object):
14 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 17 self.overlay = {}
17 18 if parentui is None:
18 19 # this is the parent of all ui children
19 20 self.parentui = None
21 self.readhooks = list(readhooks)
20 22 self.cdata = ConfigParser.SafeConfigParser()
21 23 self.readconfig(util.rcpath())
22 24
@@ -34,6 +36,7 b' class ui(object):'
34 36 else:
35 37 # parentui may point to an ui object which is already a child
36 38 self.parentui = parentui.parentui or parentui
39 self.readhooks = list(parentui.readhooks or readhooks)
37 40 parent_cdata = self.parentui.cdata
38 41 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 42 # make interpolation work
@@ -78,6 +81,8 b' class ui(object):'
78 81 for name, path in self.configitems("paths"):
79 82 if path and "://" not in path and not os.path.isabs(path):
80 83 self.cdata.set("paths", name, os.path.join(root, path))
84 for hook in self.readhooks:
85 hook(self)
81 86
82 87 def setconfig(self, section, name, val):
83 88 self.overlay[(section, name)] = val
@@ -169,17 +174,6 b' class ui(object):'
169 174 result[key.lower()] = value
170 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 177 def username(self):
184 178 """Return default username to be used in commits.
185 179
@@ -197,7 +191,7 b' class ui(object):'
197 191 user = os.environ.get("EMAIL")
198 192 if user is None:
199 193 try:
200 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
194 user = '%s@%s' % (util.getuser(), socket.getfqdn())
201 195 except KeyError:
202 196 raise util.Abort(_("Please specify a username."))
203 197 return user
@@ -217,12 +211,6 b' class ui(object):'
217 211 path = self.config("paths", default)
218 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 214 def write(self, *args):
227 215 if self.header:
228 216 if self.header != self.prev_header:
@@ -298,62 +286,6 b' class ui(object):'
298 286
299 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 289 def print_exc(self):
358 290 '''print exception traceback if traceback printing enabled.
359 291 only to call in exception handler. returns true if traceback
@@ -2,6 +2,8 b''
2 2 util.py - Mercurial utility functions and platform specfic implementations
3 3
4 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 8 This software may be used and distributed according to the terms
7 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 15 from i18n import gettext as _
14 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 18 demandload(globals(), "os threading time")
17 19
18 20 # used by parsedate
@@ -93,23 +95,6 b' def find_in_path(name, path, default=Non'
93 95 return p_name
94 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 98 def binary(s):
114 99 """return true if a string is binary data using diff's heuristic"""
115 100 if s and '\0' in s[:4096]:
@@ -510,6 +495,20 b' def is_win_9x():'
510 495 except AttributeError:
511 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 512 # Platform specific variants
514 513 if os.name == 'nt':
515 514 demandload(globals(), "msvcrt")
@@ -593,6 +592,9 b" if os.name == 'nt':"
593 592 def samestat(s1, s2):
594 593 return False
595 594
595 def shellquote(s):
596 return '"%s"' % s.replace('"', '\\"')
597
596 598 def explain_exit(code):
597 599 return _("exited with status %d") % code, code
598 600
@@ -682,6 +684,9 b' else:'
682 684 else:
683 685 raise
684 686
687 def shellquote(s):
688 return "'%s'" % s.replace("'", "'\\''")
689
685 690 def testpid(pid):
686 691 '''return False if pid dead, True if running or not sure'''
687 692 try:
@@ -982,3 +987,11 b' def bytecount(nbytes):'
982 987 if nbytes >= divisor * multiplier:
983 988 return format % (nbytes / float(divisor))
984 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 297 win32file.SetEndOfFile(self.handle)
298 298 except pywintypes.error, err:
299 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 2 # Author(s):
3 3 # Thomas Arendsen Hein <thomas@intevation.de>
4 4 #
@@ -20,7 +20,7 b''
20 20 </div>
21 21
22 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 24 <br/>
25 25 #changenav%naventry#<br/>
26 26 </div>
@@ -6,6 +6,7 b''
6 6 <body>
7 7
8 8 <div class="buttons">
9 <a href="?sl=#rev#">shortlog</a>
9 10 <a href="?cmd=tags">tags</a>
10 11 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 12 #archives%archiveentry#
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 </div>
15 15
16 16 <div>
@@ -5,6 +5,7 b''
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 9 <a href="?cmd=tags">tags</a>
9 10 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 11 <a href="?cs=#node|short#;style=raw">raw</a>
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 </div>
15 15
16 16 <div>
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 </div>
15 15
16 16 <div class="title">#file|escape#</div>
@@ -5,6 +5,7 b''
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 9 <a href="?tags=">tags</a>
9 10 <a href="?cs=#node|short#">changeset</a>
10 11 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
@@ -5,6 +5,7 b''
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 9 <a href="?tags=">tags</a>
9 10 <a href="?cs=#node|short#">changeset</a>
10 11 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 </div>
15 15
16 16 <div class="title" >#file|urlescape#</div>
@@ -8,6 +8,7 b''
8 8
9 9 <div class="buttons">
10 10 <a href="?cl=tip">changelog</a>
11 <a href="?sl=tip">shortlog</a>
11 12 <a href="?tags=">tags</a>
12 13 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
13 14 <a href="?fa=#filenode|short#;file=#file|urlescape#">annotate</a>
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 </div>
15 15
16 16 <div class="title">#file|escape#</div>
@@ -5,6 +5,7 b''
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 9 <a href="?tags=">tags</a>
9 10 <a href="?cs=#node|short#">changeset</a>
10 11 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 </div>
15 15
16 16 <div class="title" >#path|escape#</div>
@@ -5,6 +5,7 b''
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=#rev#">changelog</a>
8 <a href="?sl=#rev#">shortlog</a>
8 9 <a href="?tags=">tags</a>
9 10 <a href="?cs=#node|short#">changeset</a>
10 11 #archives%archiveentry#
@@ -3,7 +3,10 b' header = header.tmpl'
3 3 footer = footer.tmpl
4 4 search = search.tmpl
5 5 changelog = changelog.tmpl
6 shortlog = shortlog.tmpl
7 shortlogentry = shortlogentry.tmpl
6 8 naventry = '<a href="?cl=#rev#">#label|escape#</a> '
9 navshortentry = '<a href="?sl=#rev#">#label|escape#</a> '
7 10 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
8 11 filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> '
9 12 fileellipses = '...'
@@ -1,6 +1,6 b''
1 1 #header#
2 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 4 </div>
5 5
6 6 <h2>searching for #query|escape#</h2>
@@ -5,6 +5,7 b''
5 5
6 6 <div class="buttons">
7 7 <a href="?cl=tip">changelog</a>
8 <a href="?sl=tip">shortlog</a>
8 9 <a href="?tags=">tags</a>
9 10 <a href="?mf=#manifest|short#;path=/">manifest</a>
10 11 </div>
@@ -1,13 +1,32 b''
1 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 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 26 </div>
8 27
9 28 <table cellspacing="0">
10 #entries#
29 #entries%shortlogentry#
11 30 </table>
12 31
13 32 #footer#
@@ -57,6 +57,12 b' pre { margin: 0; }'
57 57 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
58 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 66 /* Tag entries */
61 67 #tagEntries { list-style: none; margin: 0; padding: 0; }
62 68 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
@@ -9,7 +9,8 b''
9 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 10 </div>
11 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 14 </div>
14 15
15 16 <div class="title">&nbsp;</div>
@@ -10,7 +10,7 b''
10 10 </div>
11 11
12 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 14 <br/>
15 15 </div>
16 16
@@ -7,6 +7,7 b''
7 7
8 8 <div class="buttons">
9 9 <a href="?cl=tip">changelog</a>
10 <a href="?sl=tip">shortlog</a>
10 11 <a href="?mf=#manifest|short#;path=/">manifest</a>
11 12 <a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a>
12 13 </div>
@@ -28,6 +28,6 b' writing tests:'
28 28
29 29 - diff will show the current time
30 30
31 use hg diff | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" to strip
32 dates
33
31 use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
32 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
33 to strip dates
@@ -25,7 +25,7 b' parser = optparse.OptionParser("%prog [o'
25 25 parser.add_option("-v", "--verbose", action="store_true",
26 26 help="output verbose messages")
27 27 parser.add_option("-t", "--timeout", type="int",
28 help="output verbose messages")
28 help="kill errant tests after TIMEOUT seconds")
29 29 parser.add_option("-c", "--cover", action="store_true",
30 30 help="print a test coverage report")
31 31 parser.add_option("-s", "--cover_stdlib", action="store_true",
@@ -201,6 +201,11 b' def run(cmd):'
201 201 return ret, splitnewlines(output)
202 202
203 203 def run_one(test):
204 '''tristate output:
205 None -> skipped
206 True -> passed
207 False -> failed'''
208
204 209 vlog("# Test", test)
205 210 if not verbose:
206 211 sys.stdout.write('.')
@@ -217,15 +222,28 b' def run_one(test):'
217 222 os.mkdir(tmpd)
218 223 os.chdir(tmpd)
219 224
220 if test.endswith(".py"):
221 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
222 else:
223 cmd = '"%s"' % (os.path.join(TESTDIR, test))
225 lctest = test.lower()
224 226
225 # To reliably get the error code from batch files on WinXP,
226 # the "cmd /c call" prefix is needed. Grrr
227 if os.name == 'nt' and test.endswith(".bat"):
227 if lctest.endswith('.py'):
228 cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
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 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 248 if options.timeout > 0:
231 249 signal.alarm(options.timeout)
@@ -244,7 +262,7 b' def run_one(test):'
244 262 ref_out = splitnewlines(f.read())
245 263 f.close()
246 264 else:
247 ref_out = ['']
265 ref_out = []
248 266 if out != ref_out:
249 267 diffret = 1
250 268 print "\nERROR: %s output changed" % (test)
@@ -330,16 +348,23 b' try:'
330 348
331 349 tests = 0
332 350 failed = 0
351 skipped = 0
333 352
334 353 if len(args) == 0:
335 354 args = os.listdir(".")
336 355 for test in args:
337 if test.startswith("test-") and not '~' in test and not '.' in test:
338 if not run_one(test):
356 if (test.startswith("test-") and '~' not in test and
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 363 failed += 1
340 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 368 if coverage:
344 369 output_coverage()
345 370 except KeyboardInterrupt:
@@ -27,7 +27,7 b' adding b'
27 27 reverting a
28 28 changeset 3:4cbb1e70196a backs out changeset 1:22bca4c721e5
29 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 31 b: No such file or directory
32 32 adding a
33 33 adding b
@@ -30,14 +30,20 b' cd ..'
30 30 hg init empty
31 31 hg -R test bundle full.hg empty
32 32 hg -R test unbundle full.hg
33 hg -R empty unbundle full.hg
34 33 hg -R empty heads
35 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 41 rm -rf empty
38 42 hg init empty
39 43 cd empty
40 44 hg -R bundle://../full.hg log
45 echo '[hooks]' >> .hg/hgrc
46 echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
41 47 #doesn't work (yet ?)
42 48 #hg -R bundle://../full.hg verify
43 49 hg pull bundle://../full.hg
@@ -11,28 +11,34 b' adding manifests'
11 11 adding file changes
12 12 added 0 changesets with 0 changes to 4 files
13 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 29 adding changesets
15 30 adding manifests
16 31 adding file changes
17 32 added 9 changesets with 7 changes to 4 files (+1 heads)
18 33 (run 'hg heads' to see heads, 'hg merge' to merge)
19 changeset: 8:836ac62537ab
20 tag: tip
21 parent: 3:ac69c658229d
22 user: test
23 date: Mon Jan 12 13:46:40 1970 +0000
24 summary: 0.3m
25
26 changeset: 7:80fe151401c2
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
34 rolling back last transaction
35 pulling from ../full.hg
36 requesting all changes
37 adding changesets
38 adding manifests
39 adding file changes
40 added 9 changesets with 7 changes to 4 files (+1 heads)
41 (run 'hg heads' to see heads, 'hg merge' to merge)
36 42 changeset: 8:836ac62537ab
37 43 tag: tip
38 44 parent: 3:ac69c658229d
@@ -81,6 +87,7 b' user: test'
81 87 date: Mon Jan 12 13:46:40 1970 +0000
82 88 summary: 0.0
83 89
90 changegroup: u=bundle:../full.hg
84 91 pulling from bundle://../full.hg
85 92 requesting all changes
86 93 adding changesets
@@ -45,7 +45,7 b' hg --cwd c head -v'
45 45 hg --cwd b tip --verbose
46 46
47 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 49 hg --cwd c --config '' tip -q
50 50 hg --cwd c --config a.b tip -q
51 51 hg --cwd c --config a tip -q
@@ -18,6 +18,13 b' head -n 3 port > port1'
18 18 mv port1 port
19 19 hg commit -m 4 -u spam -d '4 0'
20 20 hg grep port port
21 echo 'FIXME: history is wrong here'
22 21 hg grep --all -nu port port
23 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 1 port:4:export
2 2 port:4:vaportight
3 3 port:4:import/export
4 FIXME: history is wrong here
5 port:1:1:-:eggs:import
6 port:1:2:+:eggs:vaportight
7 port:1:3:+:eggs:import/export
8 port:0:2:+:spam:export
9 port:0:1:+:spam:import
4 port:4:4:-:spam:import/export
5 port:3:4:+:eggs:import/export
6 port:2:1:-:spam:import
7 port:2:2:-:spam:export
8 port:2:1:+:spam:export
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 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 178 -r --rev revision
179 179 -a --text treat all files as text
180 180 -p --show-function show which function each change is in
181 -g --git use git extended diff format
181 182 -w --ignore-all-space ignore white space when comparing lines
182 183 -b --ignore-space-change ignore changes in the amount of white space
183 184 -B --ignore-blank-lines ignore changes whose lines are all blank
@@ -187,13 +188,15 b' hg status [OPTION]... [FILE]...'
187 188
188 189 show changed files in the working directory
189 190
190 Show changed files in the repository. If names are
191 given, only files that match are shown.
191 Show status of files in the repository. If names are given, only
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 195 The codes used to show the status of files are:
194 196 M = modified
195 197 A = added
196 198 R = removed
199 C = clean
197 200 ! = deleted, but still tracked
198 201 ? = not tracked
199 202 I = ignored (not shown by default)
@@ -203,10 +206,12 b' aliases: st'
203 206
204 207 options:
205 208
209 -A --all show status of all files
206 210 -m --modified show only modified files
207 211 -a --added show only added files
208 212 -r --removed show only removed files
209 213 -d --deleted show only deleted (but tracked) files
214 -c --clean show only files without changes
210 215 -u --unknown show only unknown (not tracked) files
211 216 -i --ignored show ignored files
212 217 -n --no-status hide status prefix
@@ -17,9 +17,9 b' cd ../b'
17 17
18 18 # changegroup hooks can see env vars
19 19 echo '[hooks]' > .hg/hgrc
20 echo 'prechangegroup = echo prechangegroup hook' >> .hg/hgrc
21 echo 'changegroup = echo changegroup hook: n=$HG_NODE' >> .hg/hgrc
22 echo 'incoming = echo incoming hook: n=$HG_NODE' >> .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 u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
22 echo 'incoming = echo incoming hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
23 23
24 24 # pretxncommit and commit hooks can see both parents of merge
25 25 cd ../a
@@ -22,11 +22,11 b' pretxncommit hook: n=4c52fb2e402287dd5dc'
22 22 3:4c52fb2e4022
23 23 commit hook: n=4c52fb2e402287dd5dc052090682536c8406c321 p1=1324a5531bac09b329c3845d35ae6a7526874edb p2=b702efe9688826e3a91283852b328b84dbf37bc2
24 24 commit hook b
25 prechangegroup hook
26 changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2
27 incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2
28 incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb
29 incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321
25 prechangegroup hook: u=file:
26 changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file:
27 incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file:
28 incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb u=file:
29 incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321 u=file:
30 30 pulling from ../a
31 31 searching for changes
32 32 adding changesets
@@ -4,22 +4,31 b' hg init test'
4 4 cd test
5 5 echo foo>foo
6 6 hg commit -A -d '0 0' -m 1
7 hg --config server.uncompressed=True serve -p 20059 -d --pid-file=hg1.pid
8 cat hg1.pid >> $DAEMON_PIDS
9 hg serve -p 20060 -d --pid-file=hg2.pid
10 cat hg2.pid >> $DAEMON_PIDS
7 hg --config server.uncompressed=True serve -p 20059 -d --pid-file=../hg1.pid
8 hg serve -p 20060 -d --pid-file=../hg2.pid
11 9 cd ..
10 cat hg1.pid hg2.pid >> $DAEMON_PIDS
12 11
13 12 echo % clone via stream
14 13 http_proxy= hg clone --uncompressed http://localhost:20059/ copy 2>&1 | \
15 14 sed -e 's/[0-9][0-9.]*/XXX/g'
16 cd copy
17 hg verify
15 hg verify -R copy
18 16
19 17 echo % try to clone via stream, should use pull instead
20 18 http_proxy= hg clone --uncompressed http://localhost:20060/ copy2
21 19
22 20 echo % clone via pull
23 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 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 28 crosschecking files in changesets and manifests
29 29 checking files
30 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 1 #!/bin/sh
2 2
3 3 hg init a
4 mkdir a/d1
5 mkdir a/d1/d2
4 6 echo line 1 > a/a
7 echo line 1 > a/d1/d2/a
5 8 hg --cwd a ci -d '0 0' -Ama
6 9
7 10 echo line 2 >> a/a
@@ -69,7 +72,7 b' rm -rf b'
69 72
70 73 echo % plain diff in email, no subject, no message body, should fail
71 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 76 rm -rf b
74 77
75 78 echo % hg export in email, should use patch header
@@ -79,3 +82,20 b' python mkmsg.py | hg --cwd b import -'
79 82 hg --cwd b tip | grep second
80 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 1 adding a
2 adding d1/d2/a
2 3 % import exported patch
3 4 requesting all changes
4 5 adding changesets
5 6 adding manifests
6 7 adding file changes
7 added 1 changesets with 1 changes to 1 files
8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 added 1 changesets with 2 changes to 2 files
9 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 10 applying ../tip.patch
10 patching file a
11 11 % message should be same
12 12 summary: second change
13 13 % committer should be same
@@ -17,10 +17,9 b' requesting all changes'
17 17 adding changesets
18 18 adding manifests
19 19 adding file changes
20 added 1 changesets with 1 changes to 1 files
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 added 1 changesets with 2 changes to 2 files
21 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 22 applying ../tip.patch
23 patching file a
24 23 transaction abort!
25 24 rollback completed
26 25 % import of plain diff should be ok with message
@@ -28,38 +27,34 b' requesting all changes'
28 27 adding changesets
29 28 adding manifests
30 29 adding file changes
31 added 1 changesets with 1 changes to 1 files
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 added 1 changesets with 2 changes to 2 files
31 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 32 applying ../tip.patch
34 patching file a
35 33 % import from stdin
36 34 requesting all changes
37 35 adding changesets
38 36 adding manifests
39 37 adding file changes
40 added 1 changesets with 1 changes to 1 files
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 added 1 changesets with 2 changes to 2 files
39 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 40 applying patch from stdin
43 patching file a
44 41 % override commit message
45 42 requesting all changes
46 43 adding changesets
47 44 adding manifests
48 45 adding file changes
49 added 1 changesets with 1 changes to 1 files
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 added 1 changesets with 2 changes to 2 files
47 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 48 applying patch from stdin
52 patching file a
53 49 summary: override
54 50 % plain diff in email, subject, message body
55 51 requesting all changes
56 52 adding changesets
57 53 adding manifests
58 54 adding file changes
59 added 1 changesets with 1 changes to 1 files
60 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 added 1 changesets with 2 changes to 2 files
56 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 57 applying ../msg.patch
62 patching file a
63 58 user: email patcher
64 59 summary: email patch
65 60 % plain diff in email, no subject, message body
@@ -67,28 +62,25 b' requesting all changes'
67 62 adding changesets
68 63 adding manifests
69 64 adding file changes
70 added 1 changesets with 1 changes to 1 files
71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 added 1 changesets with 2 changes to 2 files
66 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 67 applying patch from stdin
73 patching file a
74 68 % plain diff in email, subject, no message body
75 69 requesting all changes
76 70 adding changesets
77 71 adding manifests
78 72 adding file changes
79 added 1 changesets with 1 changes to 1 files
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
73 added 1 changesets with 2 changes to 2 files
74 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 75 applying patch from stdin
82 patching file a
83 76 % plain diff in email, no subject, no message body, should fail
84 77 requesting all changes
85 78 adding changesets
86 79 adding manifests
87 80 adding file changes
88 added 1 changesets with 1 changes to 1 files
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 added 1 changesets with 2 changes to 2 files
82 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 83 applying patch from stdin
91 patching file a
92 84 transaction abort!
93 85 rollback completed
94 86 % hg export in email, should use patch header
@@ -96,8 +88,20 b' requesting all changes'
96 88 adding changesets
97 89 adding manifests
98 90 adding file changes
99 added 1 changesets with 1 changes to 1 files
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 added 1 changesets with 2 changes to 2 files
92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 93 applying patch from stdin
102 patching file a
103 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 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 2 removing b
3 this update spans a branch affecting the following files:
4 b
5 aborting update spanning branches!
6 (use 'hg merge' to merge across branches or 'hg update -C' to lose changes)
3 abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
@@ -22,7 +22,7 b' added 1 changesets with 1 changes to 1 f'
22 22 (run 'hg heads' to see heads, 'hg merge' to merge)
23 23 merge: warning: conflicts during merge
24 24 resolving manifests
25 force False allow True moddirstate True linear False
25 overwrite None branchmerge True partial False linear False
26 26 ancestor 055d847dd401 local 2eded9ab0a5c remote 84cf5750dd20
27 27 test.txt versions differ, resolve
28 28 merging test.txt
@@ -36,13 +36,19 b' kill `cat hg.pid`'
36 36
37 37 echo % expect success
38 38 echo 'allow_push = *' >> .hg/hgrc
39 echo '[hooks]' >> .hg/hgrc
40 echo 'changegroup = echo changegroup: u=$HG_URL >> $HGTMP/urls' >> .hg/hgrc
39 41 hg serve -p 20059 -d --pid-file=hg.pid
40 42 cat hg.pid >> $DAEMON_PIDS
41 43 hg --cwd ../test2 push http://localhost:20059/
42 44 kill `cat hg.pid`
43 45 hg rollback
44 46
47 sed 's/\(remote:http.*\):.*/\1/' $HGTMP/urls
48
45 49 echo % expect authorization error: all users denied
50 echo '[web]' > .hg/hgrc
51 echo 'push_ssl = false' >> .hg/hgrc
46 52 echo 'deny_push = *' >> .hg/hgrc
47 53 hg serve -p 20059 -d --pid-file=hg.pid
48 54 cat hg.pid >> $DAEMON_PIDS
@@ -20,6 +20,7 b' adding manifests'
20 20 adding file changes
21 21 added 1 changesets with 1 changes to 1 files
22 22 rolling back last transaction
23 changegroup: u=remote:http
23 24 % expect authorization error: all users denied
24 25 pushing to http://localhost:20059/
25 26 searching for changes
@@ -17,6 +17,8 b' if [ ! -x dummyssh ] ; then'
17 17 exit -1
18 18 fi
19 19
20 SSH_CLIENT='127.0.0.1 1 2'
21 export SSH_CLIENT
20 22 echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog
21 23 $2
22 24 EOF
@@ -29,6 +31,8 b' echo this > foo'
29 31 hg ci -A -m "init" -d "1000000 0" foo
30 32 echo '[server]' > .hg/hgrc
31 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 37 cd ..
34 38
@@ -46,6 +50,9 b' echo "# verify"'
46 50 cd local
47 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 56 echo "# empty default pull"
50 57 hg paths
51 58 hg pull -e ../dummyssh
@@ -83,5 +83,7 b' Got arguments 1:user@dummy 2:hg -R remot'
83 83 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
84 84 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
85 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 87 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
87 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 37 cd local
38 38 hg verify
39 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 48 http_proxy= hg pull
41 49
42 50 kill $!
@@ -19,6 +19,12 b' crosschecking files in changesets and ma'
19 19 checking files
20 20 1 files, 1 changesets, 1 total revisions
21 21 foo
22 adding quux
23 changegroup: u=static-http://localhost:20059/remote
22 24 pulling from static-http://localhost:20059/remote
23 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 35 hg copy modified copied
36 36 echo "hg status -C:"
37 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 108 R removed
109 109 ! deleted
110 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 19 cat .hgtags
20 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 27 hg tag -l 'xx
23 28 newline'
24 29 hg tag -l 'xx:xx'
@@ -25,5 +25,8 b" use of 'hg tag NAME [REV]' is deprecated"
25 25 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
26 26 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
27 27 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
28 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
29 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
30 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
28 31 abort: '\n' cannot be used in a tag name
29 32 abort: ':' cannot be used in a tag name
@@ -15,7 +15,7 b' date: Mon Jan 12 13:46:40 1970 +0'
15 15 summary: 1
16 16
17 17 resolving manifests
18 force None allow None moddirstate True linear True
18 overwrite False branchmerge False partial False linear True
19 19 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
20 20 a versions differ, resolve
21 21 remote created b
@@ -31,7 +31,7 b' date: Mon Jan 12 13:46:40 1970 +0'
31 31 summary: 2
32 32
33 33 resolving manifests
34 force None allow None moddirstate True linear True
34 overwrite False branchmerge False partial False linear True
35 35 ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
36 36 remote deleted b
37 37 removing b
@@ -41,7 +41,7 b' user: test'
41 41 date: Mon Jan 12 13:46:40 1970 +0000
42 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 45 failed
46 46 changeset: 0:33aaa84a386b
47 47 user: test
@@ -49,7 +49,7 b' date: Mon Jan 12 13:46:40 1970 +0'
49 49 summary: 1
50 50
51 51 resolving manifests
52 force None allow None moddirstate True linear True
52 overwrite False branchmerge False partial False linear True
53 53 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
54 54 a versions differ, resolve
55 55 remote created b
@@ -95,21 +95,12 b' user: test'
95 95 date: Mon Jan 12 13:46:40 1970 +0000
96 96 summary: 2
97 97
98 resolving manifests
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)
98 abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
108 99 failed
109 100 abort: outstanding uncommitted changes
110 101 failed
111 102 resolving manifests
112 force False allow True moddirstate True linear False
103 overwrite False branchmerge True partial False linear False
113 104 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
114 105 a versions differ, resolve
115 106 b versions differ, resolve
@@ -40,7 +40,7 b' a'
40 40 side1
41 41 side2
42 42 resolving manifests
43 force 1 allow None moddirstate True linear False
43 overwrite True branchmerge False partial False linear False
44 44 ancestor 8515d4bfda76 local 1c0f48f8ece6 remote 0594b9004bae
45 45 remote deleted side2, clobbering
46 46 remote deleted side1, clobbering
General Comments 0
You need to be logged in to leave comments. Login now