Merge lp:~keegan-csmith/ibid/hg-505320 into lp:~ibid-core/ibid/old-trunk-1.6

Proposed by Keegan Carruthers-Smith
Status: Needs review
Proposed branch: lp:~keegan-csmith/ibid/hg-505320
Merge into: lp:~ibid-core/ibid/old-trunk-1.6
Diff against target: 251 lines (+176/-13)
5 files modified
ibid/plugins/bzr.py (+2/-1)
ibid/plugins/hg.py (+155/-0)
ibid/plugins/svn.py (+1/-1)
ibid/utils/__init__.py (+17/-11)
setup.py (+1/-0)
To merge this branch: bzr merge lp:~keegan-csmith/ibid/hg-505320
Reviewer Review Type Date Requested Status
Max Rabkin Needs Fixing
Review via email: mp+50535@code.launchpad.net

Description of the change

Mercurial plugin

To post a comment you must log in.
Revision history for this message
Max Rabkin (max-rabkin) wrote :

@match(r'^(?:hgrepos|hgrepositories)$') is not very human-friendly, and it should use simple matches (as should the other regex).

def get_cache_directory(plugin_name = None)

PEP 8 doesn't have spaces around "=" here.

I haven't looked at the code itself yet.

review: Needs Fixing
lp:~keegan-csmith/ibid/hg-505320 updated
1009. By Keegan Carruthers-Smith

PEP 8 related changes.

1010. By Keegan Carruthers-Smith

Simplified match regex for commit and made how the revno is displayed consistent with how the HG UI does it.

Revision history for this message
Keegan Carruthers-Smith (keegan-csmith) wrote :

> @match(r'^(?:hgrepos|hgrepositories)$') is not very human-friendly, and it
> should use simple matches (as should the other regex).
r1010. Not changing hgrepos|hgrepositories since that is the same syntax used by the bzr and svn plugin.

> def get_cache_directory(plugin_name = None)
>
> PEP 8 doesn't have spaces around "=" here.
r1009

lp:~keegan-csmith/ibid/hg-505320 updated
1011. By Keegan Carruthers-Smith

Made repo list command for hg, bzr and svn optionally contain a space.

Revision history for this message
Max Rabkin (max-rabkin) wrote :

Is it possible to make a "repositories" command that lists all svn, bzr and hg repos?

Have you tested this?

Do svn and bzr cooperate with "# Don't respond because this repo might be matched by bzr/svn/etc"?

Is it possible to refactor some of the common code between the three?

Revision history for this message
Keegan Carruthers-Smith (keegan-csmith) wrote :

> Is it possible to make a "repositories" command that lists all svn, bzr and hg
> repos?
I am going to re-evaulate anyvc, and then we can have a generic vc plugin which would do that. At the moment I suppose we could do it (assuming a certian ibid feature exists). But let me relook at anyvc first.

> Have you tested this?
Yup, works for me :)

> Do svn and bzr cooperate with "# Don't respond because this repo might be
> matched by bzr/svn/etc"?
Yes

> Is it possible to refactor some of the common code between the three?
Yes, but as above a more ideal solution would be using anyvc. See bug #505891

Revision history for this message
Keegan Carruthers-Smith (keegan-csmith) wrote :

I just realised this might act a bit funny when a remote repo deletes some revisions. I'll test it out

Revision history for this message
Keegan Carruthers-Smith (keegan-csmith) wrote :

Ok so when deleting changesets the user does not get informed about it, nor does ibid forget the hashes of the deleted changesets. Basically the only time ibid will announce anything, is when it sees a changeset with a hash it does not recognize. This only applies to remote repos.

Revision history for this message
Stefano Rivera (stefanor) wrote :

OK, so what's the plan re multiple repository plugins?

Revision history for this message
Keegan Carruthers-Smith (keegan-csmith) wrote :

> OK, so what's the plan re multiple repository plugins?
A wishlist ticket?

Unmerged revisions

1011. By Keegan Carruthers-Smith

Made repo list command for hg, bzr and svn optionally contain a space.

1010. By Keegan Carruthers-Smith

Simplified match regex for commit and made how the revno is displayed consistent with how the HG UI does it.

1009. By Keegan Carruthers-Smith

PEP 8 related changes.

1008. By Keegan Carruthers-Smith

hg plugin now pulls remote repos quietly.

1007. By Keegan Carruthers-Smith

Added call to Processor.setup(self) in both Mercurial and Bazaar plugins.

1006. By Keegan Carruthers-Smith

hg plugin now supports remote repos. Also updated to work against current ibid trunk.

1005. By Keegan Carruthers-Smith

Mercurial polling now works with local repositories.

1004. By Keegan Carruthers-Smith

Added a largely untested mercurial plugin.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ibid/plugins/bzr.py'
2--- ibid/plugins/bzr.py 2010-03-27 15:50:58 +0000
3+++ ibid/plugins/bzr.py 2011-02-21 16:10:37 +0000
4@@ -75,6 +75,7 @@
5 RPC.__init__(self)
6
7 def setup(self):
8+ Processor.setup(self)
9 self.branches = {}
10 must_monitor = False
11 for name, repository in self.repositories.items():
12@@ -89,7 +90,7 @@
13 if must_monitor:
14 self.seen_revisions = {}
15
16- @match(r'^(?:repos|repositories)$')
17+ @match(r'bzr\s*repos(?:itories)')
18 def handle_repositories(self, event):
19 repositories = self.branches.keys()
20 if repositories:
21
22=== added file 'ibid/plugins/hg.py'
23--- ibid/plugins/hg.py 1970-01-01 00:00:00 +0000
24+++ ibid/plugins/hg.py 2011-02-21 16:10:37 +0000
25@@ -0,0 +1,155 @@
26+# Copyright (c) 2011, Keegan Carruthers-Smith
27+# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
28+
29+from datetime import datetime
30+
31+import logging
32+import os
33+import os.path
34+
35+from mercurial import hg, ui, commands
36+
37+from ibid.config import DictOption, IntOption
38+from ibid.plugins import Processor, match, RPC, periodic
39+from ibid.utils import ago, format_date, human_join, get_cache_directory
40+
41+features = {'hg': {
42+ 'description': u'Retrieves commit logs from a Mercurial repository.',
43+ 'categories': ('development',),
44+}}
45+
46+class Mercurial(Processor, RPC):
47+ usage = u"""(last commit|commit <revno>) [to <repo>]
48+ (hgrepos|hgrepositories)"""
49+ features = ('hg',)
50+ autoload = False
51+
52+ repositories = DictOption('repositories',
53+ 'Dict of repositories names and URLs',
54+ {})
55+ interval = IntOption('interval',
56+ 'Interval inbetween checks for new revisions',
57+ 300)
58+ max_commits_to_announce = IntOption('max_commits_to_announce',
59+ 'Maximum number of commits to announce'
60+ ' when polling for new commits.',
61+ 5)
62+
63+
64+ def __init__(self, name):
65+ self.log = logging.getLogger('plugins.hg')
66+ Processor.__init__(self, name)
67+ RPC.__init__(self)
68+
69+ def setup(self):
70+ Processor.setup(self)
71+ must_monitor = any(repo.get('poll', 'False').lower() in ('yes', 'true')
72+ for repo in self.repositories.values())
73+ self.check.im_func.disabled = not must_monitor
74+ if must_monitor:
75+ self.latest_rev = {}
76+
77+ @match(r'hg\s*repos(?:itories)')
78+ def handle_repositories(self, event):
79+ if self.repositories:
80+ repos = human_join(sorted(self.repositories.keys()))
81+ event.addresponse(u'I know about: %s', repos)
82+ else:
83+ event.addresponse(u"I don't know about any repositories")
84+
85+ def get_repo(self, ui, name, url, pull):
86+ if url[0] in (os.sep, os.altsep):
87+ return hg.repository(ui, url)
88+
89+ ui.quiet = True
90+
91+ plugin_dir = get_cache_directory('hg')
92+ repo_dir = os.path.abspath(os.path.join(plugin_dir, name))
93+
94+ if not os.path.exists(repo_dir):
95+ self.log.info('Creating a clone of %s at %s', url, repo_dir)
96+ remote_repo = hg.repository(ui, url)
97+ commands.clone(ui, remote_repo, repo_dir.encode('utf-8'),
98+ noupdate=True)
99+
100+ repo = hg.repository(ui, repo_dir)
101+ repo.ui.quiet = True
102+
103+ if pull:
104+ self.log.debug('Pulling from %s to %s', url, repo_dir)
105+ commands.pull(ui, repo, url, force=True)
106+
107+ return repo
108+
109+ def commit(self, event, revno, repository):
110+ if repository not in self.repositories:
111+ # Don't respond because this repo might be matched by bzr/svn/etc
112+ return
113+
114+ repo = self.get_repo(ui.ui(), repository,
115+ self.repositories[repository]['url'], True)
116+ if revno is None:
117+ revno = len(repo) - 1
118+ if revno == -1:
119+ event.addresponse(u'There have been no commits')
120+ return
121+
122+ ctx = repo[revno]
123+ timestamp = datetime.utcfromtimestamp(ctx.date()[0])
124+ commit = u'Commit %d:%s by %s to %s on %s at %s: %s (%s)\n' % (
125+ ctx.rev(),
126+ ctx.hex()[:12], # HG only shows the first 12 chars of the hex str
127+ ctx.user(),
128+ repository,
129+ format_date(timestamp, 'date'),
130+ format_date(timestamp, 'time'),
131+ ctx.description().replace('\n', ' '),
132+ '; '.join(ctx.files()))
133+
134+ event.addresponse(commit, conflate=False)
135+
136+ @match(r'last commit to {chunk}')
137+ def commit_repo(self, event, repository):
138+ self.commit(event, None, repository)
139+
140+ @match(r'commit {chunk} to {chunk}')
141+ def commit_rev(self, event, revno, repository):
142+ self.commit(event, revno, repository)
143+
144+ @periodic(config_key='interval')
145+ def check(self, event):
146+ for name, repository in self.repositories.iteritems():
147+ if repository.get('poll', 'False').lower() not in ('yes', 'true'):
148+ continue
149+
150+ repo = self.get_repo(ui.ui(), name, repository['url'], True)
151+
152+ rev_end = len(repo) - 1
153+ if rev_end == -1:
154+ return
155+ rev_start = self.latest_rev.get(name, rev_end) + 1
156+ self.latest_rev[name] = rev_end
157+
158+ for rev in range(rev_start, rev_end+1)[-self.max_commits_to_announce:]:
159+ ctx = repo[rev]
160+ date = datetime.fromtimestamp(ctx.date()[0])
161+ commit = u'Commit %d:%s by %s to %s %s ago: %s\n' % (
162+ ctx.rev(),
163+ ctx.hex()[:12],
164+ ctx.user(),
165+ name,
166+ ago(datetime.now() - date, 2),
167+ ctx.description().replace('\n', ' '))
168+ event.addresponse(commit,
169+ source=repository['source'],
170+ target=repository['channel'],
171+ address=False)
172+
173+ ncommits = rev_end - rev_start + 1
174+ if self.max_commits_to_announce < ncommits:
175+ event.addresponse('and %d more commits' % ncommits,
176+ source=repository['source'],
177+ target=repository['channel'],
178+ address=False)
179+
180+# vi: set et sta sw=4 ts=4:
181
182=== modified file 'ibid/plugins/svn.py'
183--- ibid/plugins/svn.py 2010-03-27 15:50:58 +0000
184+++ ibid/plugins/svn.py 2011-02-21 16:10:37 +0000
185@@ -418,7 +418,7 @@
186 else:
187 self.branches[reponame] = CommandLineBranch(reponame, repository['url'], username = repository['username'], password = repository['password'], svn_command=self.svn_command, svn_timeout=self.svn_timeout, multiline=self.multiline)
188
189- @match(r'^svn ?(?:repos|repositories)$')
190+ @match(r'svn\s*repos(?:itories)')
191 @authorise()
192 def handle_repositories(self, event):
193 repositories = self.branches.keys()
194
195=== modified file 'ibid/utils/__init__.py'
196--- ibid/utils/__init__.py 2011-01-22 23:04:00 +0000
197+++ ibid/utils/__init__.py 2011-02-21 16:10:37 +0000
198@@ -57,6 +57,22 @@
199 text = re.sub("&(\w+);", replace, text)
200 return text
201
202+def get_cache_directory(plugin_name=None):
203+ cachedir = ibid.config.plugins.get('cachedir', None)
204+ if not cachedir:
205+ cachedir = os.path.join(ibid.options['base'], 'cache')
206+ elif cachedir[0] == "~":
207+ cachedir = os.path.expanduser(cachedir)
208+ cachedir = os.path.abspath(cachedir)
209+
210+ if plugin_name is not None:
211+ cachedir = os.path.join(cachedir, plugin_name)
212+
213+ if not os.path.isdir(cachedir):
214+ os.makedirs(cachedir)
215+
216+ return cachedir
217+
218 downloads_in_progress = defaultdict(Lock)
219 def cacheable_download(url, cachefile, headers={}, timeout=60):
220 """Download url to cachefile if it's modified since cachefile.
221@@ -75,17 +91,7 @@
222 # We do allow absolute paths, for people who know what they are doing,
223 # but the common use case should be pluginname/cachefile.
224 if cachefile[0] not in (os.sep, os.altsep):
225- cachedir = ibid.config.plugins.get('cachedir', None)
226- if not cachedir:
227- cachedir = os.path.join(ibid.options['base'], 'cache')
228- elif cachedir[0] == "~":
229- cachedir = os.path.expanduser(cachedir)
230- cachedir = os.path.abspath(cachedir)
231-
232- plugindir = os.path.join(cachedir, os.path.dirname(cachefile))
233- if not os.path.isdir(plugindir):
234- os.makedirs(plugindir)
235-
236+ plugindir = get_cache_directory(os.path.dirname(cachefile))
237 cachefile = os.path.join(cachedir, cachefile)
238
239 exists = os.path.isfile(cachefile)
240
241=== modified file 'setup.py'
242--- setup.py 2011-02-20 20:31:01 +0000
243+++ setup.py 2011-02-21 16:10:37 +0000
244@@ -48,6 +48,7 @@
245 'imdb': ['imdbpy'],
246 'memory': ['objgraph'],
247 'asciiart': ['python-aalib'],
248+ 'hg': ['mercurial'],
249 },
250 dependency_links=[
251 'http://ibid.omnia.za.net/eggs/',

Subscribers

People subscribed via source and target branches