Commits

Lukas Linhart committed d1b5054

Imported Michael's work, added some distribution garbage around

  • Participants

Comments (0)

Files changed (15)

+syntax: glob
+*.pyc
+*.ini
+*.swp
+*.orig
+.project
+.pydevproject
+*.db
+.settings
+*~
+out.err
+Originally written by Michael Kleehammer.
+Later packaged and polished by Lukas "Almad" Linhart.
+Mercurial Describe extension
+=============================
+
+Use standard setup.py install command for installation.
+
+Then, enable it by putting hgext.descibe= in [extension] section in your .hgrc
+Copyright (c) 2009 Michael Kleehammer and Lukas "Almad" Linhart
+All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the Lukas Linhart nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY Lukas Linhart ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL LUKAS LINHART BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File debian/README.Debian

+centrum-python-djangobaselibrary for debian
+-------------------------------------------

File debian/changelog

+python-hgdescribe (0.1.0) unstable; urgency=low
+
+  * Initial debian version
+
+ -- Lukas Linhart <bugs@almad.net>  Thu, 12 Nov 2009 13:07:07 +0100
+

File debian/compat

+5

File debian/control

+Source: python-hgdescribe
+Section: python
+Priority: optional
+Maintainer: Lukas Linhart <bugs@almad.net>
+Build-Depends: cdbs (>= 0.4.41), debhelper (>= 5.0.37.2), python-dev, python-support (>= 0.3)
+Standards-Version: 3.7.2
+
+Package: python-hgdescribe
+Architecture: all
+Depends: ${python:Depends}, ${misc:Depends}, mercurial 
+Description: git-like describe extension for Mercurial VCS
+

File debian/copyright

+bsd
+

File debian/pycompat

+2

File debian/pyversions

+2.3-

File debian/rules

+#!/usr/bin/make -f
+# -*- mode: makefile; coding: utf-8 -*-
+
+DEB_PYTHON_SYSTEM=pysupport
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/python-distutils.mk
+
+DEB_AUTO_CLEANUP_RCS        := yes
+#DEB_INSTALL_CHANGELOGS_ALL := CHANGE_LOG.txt

File hgdescribe/__init__.py

+VERSION = (0, 1, 0)
+
+__version__ = VERSION
+__versionstr__ = '.'.join(map(str, VERSION))
+
+# for now, propagate API
+from hgdescribe.describe import *
+

File hgdescribe/describe.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# The describe Mercurial extension, similar to the 'git describe' command.
+
+import re
+from mercurial import util, context
+from mercurial.i18n import _
+from mercurial.node import nullrev, hex, short
+
+def describe(ui, repo, **opts):
+    """show most recent tag
+
+    Finds the most recent tag reachable from the current revision.
+     
+    If the current revision has been tagged, only the tag is
+    printed:
+     
+      v1.2.3
+     
+    Otherwise the long form is printed which includes the tag, the
+    number of commits after the tag, and the node of the current
+    changeset:
+
+      v1.2.3-8-2789c05b6c3b
+     
+    If the closest, tagged revision has multiple tags, each is
+    printed on a separate line unless the --single-tag option is
+    used, in which case an error is raised.
+
+    If multiple revisions are equally reachable from the root and
+    one is on the current branch, it is chosen.  Otherwise each tag
+    from each revision is printed on a separate line unless the
+    --single-rev option is used, in which case an error is raised.
+
+    If the --prefer-branch option is used, the closest tag on the
+    current branch will override closer tags that are reachable but
+    not on the same branch.  In the example below, tag A is
+    normally chosen since it is closer to the root than B (3
+    commits vs 4 commits).  However, if --prefer-branch is used, B
+    will be chosen because it is on the the same branch.
+
+            o-A-o
+           /     \\
+        o-o-B-o-o-o-o <-- root
+
+    The --require-branch option requires the tag to be on the same
+    branch.  This is similar to the --prefer-branch option but raises an
+    error if no tags are found on the current branch.
+    """
+    if not repo.local():
+        raise util.Abort(_("must be a local repository"))
+
+    if opts['rev']:
+        ctx = context.changectx(repo, opts['rev'])
+    else:
+        ctx = context.workingctx(repo).parents()[0]
+
+    tags, count = _find_closest_tag(ui, repo, ctx, opts)
+
+    uselong = opts['long'] or (count != 0 and not opts['short'])
+
+    if uselong:
+        hexfunc = (opts['full'] or ui.debugflag) and hex or short
+        node    = hexfunc(ctx.node())
+        sep     = opts['spaces'] and ' ' or '-'
+        count   = str(count)
+
+        for tag in tags:
+            ui.write("%s\n" % sep.join([tag, count, node]))
+
+    else:
+        for tag in tags:
+            ui.write("%s\n" % tag)
+            
+
+def _find_closest_tag(ui, repo, ctx, opts):
+    """
+    Walks backwards looking for tags using a breadth-first search.
+
+    If successful, returns (tags, level) where level is the distance from the working directory
+    (current chageset == 0).  Otherwise an exception is raised.
+    """
+    prefer  = bool(opts['prefer_branch'])
+    require = bool(opts['require_branch'])
+
+    branch = repo.dirstate.branch() # The branch we're currently on
+
+    # The first tags we found.  Normally we'd return as soon as we found tags,
+    # but the --prefer-branch option requires us to continue searching in case
+    # we find tags on the current branch.
+    first_found = []     # each entry is a list of tags from a single revision
+    first_level = 0      # the level where we found first_found
+
+    level = 0             # how far from the root are we
+    limit = opts['limit'] # how far can we go
+
+    stack = [ ctx ] # revisions to search; when we hit a merge we follow both
+                    # parents unless --require-branch was used
+
+    regexp = None
+    if opts['regexp']:
+        regexp = re.compile(opts['regexp'], re.IGNORECASE)
+
+    while stack:
+        current = stack
+        stack   = []
+        found   = []  # tags found on this level
+
+        if ui.debugflag:
+            print '-' * 40
+
+        for ctx in current:
+            revbranch = ctx.branch()
+            if require and revbranch != branch:
+                continue
+
+            tags = ctx.tags()
+
+            if ui.debugflag:
+                print ctx.rev(), ' '.join(tags)
+
+            if tags and 'tip' in tags:
+                tags = tags[:]     # (copy list; we don't own it)
+                tags.remove('tip')
+
+            if tags and regexp:
+                # Remove those that don't match the regular expression
+                tags = [ tag for tag in tags if regexp.match(tag) ]
+
+            if tags:
+                if revbranch == branch:
+                    # This is an exact hit on this branch, so use it now.
+                    first_found = [ tags ]
+                    first_level = level
+                    stack = None # break out of outer loop
+                    break
+
+                found.append(tags)
+                
+            # Follow both parents backwards
+            for p in ctx.parents():
+                # print 'parent:', p
+                if p.rev() != nullrev and p not in stack:
+                    stack.append(p)
+
+        if found and not first_found:
+            first_found = found
+            first_level = level
+
+            if not prefer:
+                break
+
+        level += 1
+
+        if level == limit:
+            raise util.Abort(_("No tags found before limit (%s) reached") % limit)
+
+    if len(first_found) == 0:
+        raise util.Abort(_("No tags found on this branch"))
+
+    # first_found is a list with one element for each revision.
+    if len(first_found) > 1 and opts['single_rev']:
+        raise util.Abort(_("More than one revision matches: %s") % " ".join([ str(m) for m in first_found ]))
+
+    alltags = []
+    for tags in first_found:
+        alltags.extend(tags)
+
+    if len(alltags) > 1 and opts['single_tag']:
+        raise util.Abort(_("More than one tag matches: %s") % " ".join(alltags))
+
+    return alltags, first_level
+
+
+cmdtable = {
+    "describe" : (describe,
+                  [ ('l', 'limit',          100,  _('limit how far back to search')),
+                    ('r', 'rev',            '',   _('start from specified revision')),
+                    ('',  'long',           None, _('output long form always')),
+                    ('',  'short',          None, _('output short form always')),
+                    ('',  'full',           None, _('output full 40-digit changesetID')),
+                    ('',  'spaces',         None, _('separate long form with spaces')),
+                    ('',  'prefer-branch',  None, _('prefer tags on this branch')),
+                    ('b', 'require-branch', None, _('require tag to be on this branch')),
+                    ('',  'single-tag',     None, _('require a single tag')),
+                    ('',  'single-rev',     None, _('require a single rev')),
+                    ('r', 'regexp',         '',   _('only consider tags that match the regexp')),
+                    ],
+                  _('hg describe')),
+    }
+#!/usr/bin/env python
+
+from setuptools import setup
+from distutils.command.install_data import install_data
+from distutils.command.install import INSTALL_SCHEMES
+import os
+import sys
+
+# Dynamically calculate the version based on django.VERSION.
+version = __import__('hgdescribe').__versionstr__
+setup(
+    name = "hgdescribe",
+    version = version,
+#    url = '',
+    author = 'Lukas Linhart',
+    author_email = 'bugs@almad.net',
+    description = 'Describe extension for Mercurial VCS',
+    packages = ['hgdescribe'],
+    scripts = [],
+    classifiers=[
+        "Development Status :: 4 - Beta",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: BSD License",
+        "Operating System :: OS Independent",
+        "Programming Language :: Python :: 2.5",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+    ]
+)