Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

retrieving logs from mercurial using api

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.

like image 590
gruszczy Avatar asked Dec 16 '09 22:12

gruszczy


3 Answers

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).

like image 78
durin42 Avatar answered Oct 15 '22 23:10

durin42


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.

like image 2
Steve Losh Avatar answered Oct 16 '22 00:10

Steve Losh


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)
like image 2
AlexS Avatar answered Oct 16 '22 00:10

AlexS