I would like to retrieve logs from a mercurial repository using mercurial commands api. Unfortunately, mercurial.commands.log prints the messages to the stdout, instead of returning some nice list of revisions, like e.g. pysvn does. Can the be achieved easily? I would like to add mercurial support to my program and would like to do this as easily, as it's possible.
The simple answer is to use ui.pushbuffer()
right before you call the log command and log_output = ui.popbuffer()
right after you call it. By doing that log_output
will contain the output of the log command.
Are you actually looking for the straight log output though, or do you really want the diff or some other kind of data? If we know what exactly you're trying to get (for example: "the commit messages of every changeset between X and Y") we might be able to show you a better way.
EDIT: Take a look at the Mercurial API wiki page to see how to get most of the common information from repo
and ctx
objects.
You should do something along the lines of this:
from mercurial import ui, hg u = ui.ui() repo = hg.repo() for rev in repo: print repo[rev]
the subscripted object is a context object. It has useful methods like description()
, branch()
, and user()
. For a complete list of what it can do, see the source (or do a dir()
on it).
yeah I had the same problem.. seems as its as designed to disallow retrieving logs remotely. The web interface gives a little rss feed but it wasn't enough of a history for me. So we created our own customised rss feed...
its not the most elaborate of things and is customised to our liking, you can mix the fields around in print_item() to change the look of the feed. You could also mod it to return log info on specific changesets if needed.
You will have to add a script alias to apache, something like (See http://httpd.apache.org/docs/2.0/howto/cgi.html for more info):
ScriptAlias /feed.cgi /usr/local/systems/hg/script/feed.cgi
feed.cgi file contents:
#!/usr/bin/env python2.5
# -*- python -*-
"""
Creates a rss feed from commit log messages in a repository/branch.
Can be filtered on commit logs from a set date eg date=2009-12-12
or by a number of days previous eg. days=7
Usage:
* retrieve all logs: http://hg.server/feed.cgi?repository=MyRepo
* retrieve logs from set date: http://hg.server/feed.cgi?repository=DMyRepo&date=2009-11-11
* retrieve logs from last 77 days: http://hg.server/feed.cgi?repository=DMyRepo&days=77
* retrieve all logs from a branch: http://hg.server/feed.cgi?repository=MyRepo&branch=myBranch
Script Location on server: /usr/local/systems/hg/script/feed.cgi
"""
defaultdateformats = (
'%Y-%m-%d %H:%M:%S',
'%Y-%m-%d %I:%M:%S%p',
'%Y-%m-%d %H:%M',
'%Y-%m-%d %I:%M%p',
'%Y-%m-%d',
'%m-%d',
'%m/%d',
'%m/%d/%y',
'%m/%d/%Y',
'%a %b %d %H:%M:%S %Y',
'%a %b %d %I:%M:%S%p %Y',
'%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
'%b %d %H:%M:%S %Y',
'%b %d %I:%M:%S%p %Y',
'%b %d %H:%M:%S',
'%b %d %I:%M:%S%p',
'%b %d %H:%M',
'%b %d %I:%M%p',
'%b %d %Y',
'%b %d',
'%H:%M:%S',
'%I:%M:%S%p',
'%H:%M',
'%I:%M%p',
)
import os, sys, cgi, cgitb, datetime, time
cgitb.enable()
from mercurial import ui, hg, util
from mercurial.node import short
def find_repository(name):
base = '/usr/local/systems/hg/repos/'
path = os.path.join(base, name)
repos = hg.repository(None, path)
return repos
def find_changes(repos, branch, date):
# returns true if d2 is newer than d1
def newerDate(d1, d2):
d1 = datetime.datetime.fromtimestamp(d1)
d2 = datetime.datetime.fromtimestamp(d2)
return d1 < d2
#for ctx in repos.changelog:
# print ctx
changes = repos.changelog
out = []
# filter on branch
if branch != '':
changes = [change for change in changes if repos.changectx(change).branch() == branch ]
# filter on date
if date != '':
changes = [change for change in changes if newerDate(date, repos.changectx(change).date()[0]) ]
return changes
def print_item(change, link_template):
def _element(name, content):
content = cgi.escape(content)
print " <%(name)s>%(content)s</%(name)s>" % {
'name': name,
'content': content
}
link = link_template % {'node': short(change.node())}
print " <item>"
_element('title', str(change.rev()))
_element('description', change.description())
_element('guid', str(change.rev()))
_element('author', change.user())
_element('link', link)
_element('pubdate', str(datetime.datetime.fromtimestamp(change.date()[0])))
print " </item>"
def print_rss(changes, repos, template):
print """<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<link>N/A</link>
<language>en-us</language>
<title>Changelog</title>
<description>Changelog</description>
"""
for change in changes:
ctx = repos.changectx(change)
print_item(ctx, template)
print """
</channel>
</rss>
"""
if __name__=="__main__":
# -*- python -*-
print "Content-Type: application/rss+xml; charset=UTF-8"
print
f = cgi.FieldStorage()
if not f.has_key("repository"):
print "Need to specify repository."
sys.exit()
repository = f['repository'].value
branch = ''
if f.has_key('branch'):
branch = f['branch'].value
date = ''
if f.has_key('date') and not f.has_key('days'):
try:
#date = datetime.datetime.strptime(f['date'].value, '%Y-%m-%d')
date = util.parsedate(f['date'].value)[0]
except:
print 'Error in date format, use one of the following formats:', defaultdateformats
sys.exit()
elif f.has_key('days') and not f.has_key('date'):
days = int(f['days'].value)
try:
date = datetime.datetime.now() - datetime.timedelta(days=days)
date = time.mktime(date.timetuple())
except:
print 'Error in days, please use a standard number eg. days=7'
sys.exit()
elif f.has_key('days') and f.has_key('date'):
print 'Error, please only supply a dayrange OR a date, not both'
sys.exit()
repos = find_repository(repository)
changes = find_changes(repos, branch, date)
rev_link_template = 'http://hg.server/hg/%(repos)s/rev/%%(node)s' % {
'repos': repository
}
print_rss(changes, repos, rev_link_template)