]> asedeno.scripts.mit.edu Git - git-svn-keywords.git/blobdiff - git-svn-keywords.py
If we find a rNNNM blob, update it
[git-svn-keywords.git] / git-svn-keywords.py
index d1e6f21887e50f432aa3718b748aa4f0468e79b6..30c9cae28b660612dfe1edab024093f7f98dccd3 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python2.6
 # -*- coding: utf-8 -*-
 
 # Copyright (c) 2009 Alejandro R. SedeƱo <asedeno@mit.edu>
 # git svn keyword parsing, populating, and clearing.
 
 from __future__ import with_statement
-import ConfigParser, errno, os, re, urllib
+import errno, os, re, urllib
+from ConfigParser import ConfigParser
+from optparse import OptionParser
+from fnmatch import fnmatch
 import git
 
-VERSION = 0
+VERSION = "0.9.1"
 
 # Where we keep data in the repo.
 def gsk(g):
     return os.path.join(g.path, 'svn_keywords')
 
 #Configuration Data
-CONFIG = ConfigParser.ConfigParser()
-FILES = ConfigParser.ConfigParser()
-FILEINFO = ConfigParser.ConfigParser()
+CONFIG = ConfigParser()
+FILES = ConfigParser()
+FILEINFO = ConfigParser()
 
 CONFIG_PATH = ''
 FILES_PATH = ''
@@ -63,7 +66,66 @@ def get_svn_keyword_re(s):
         svn_keywords_re[s] = re.compile('\$(' + ('|'.join(svn_keywords[s])) + ')[^$]*\$')
     return svn_keywords_re[s]
 
+def conf_right_version():
+    ver = -1
+    if CONFIG.has_option('core', 'version'):
+        ver = CONFIG.get('core', 'version')
+    return ver == VERSION
+
+def read_file_data():
+    if conf_right_version():
+        FILES.read(FILES_PATH)
+
+def get_last_rev(path):
+    if not CONFIG.has_section(path):
+        CONFIG.add_section(path)
+
+    lastrev = None
+    if conf_right_version() and CONFIG.has_option(path, 'lastrev'):
+        try:
+            lastrev = CONFIG.getint(path, 'lastrev')
+        except ValueError:
+            lastrev = None
+    return lastrev
+
+
 # Parse the unhandled log.
+def _do_parse_unhandled(directory):
+    base = os.path.join(directory)
+    for d in os.listdir(base):
+        subent = os.path.join(base, d)
+        if (d == 'unhandled.log' and os.path.isfile(subent)):
+            rev = None
+            strip_prefix = g.git.config('--get','svn-remote.svn.fetch').split(':')[0]
+            lastrev = get_last_rev(subent)
+            with open(subent, 'r') as f:
+                # Compile the regular expressions we'll be using here.
+                re_rev = re.compile("^r(\d+)$")
+                re_keywords = re.compile("^\s+[-+]file_prop: (\S+) svn:keywords ?(\S*)$")
+
+                for line in f:
+                    m = re_rev.match(line)
+                    if m:
+                        rev = m.group(1)
+                        continue
+
+                    if (lastrev >= int(rev)):
+                        continue
+
+                    m = re_keywords.match(line)
+                    if m:
+                        path = urllib.unquote(m.group(1))
+                        path = os.path.relpath(path, strip_prefix)
+                        keywords = set(urllib.unquote(m.group(2)).split(' '))
+                        if not FILES.has_section(path):
+                            FILES.add_section(path)
+                        FILES.set(path, rev, keywords)
+            if rev:
+                lastrev = max(int(rev), lastrev)
+                CONFIG.set(subent, 'lastrev', lastrev)
+        elif (os.path.isdir(subent)):
+            _do_parse_unhandled(subent)
+
 def parse_svn_unhandled(g):
     try:
         os.mkdir(gsk(g))
@@ -71,42 +133,8 @@ def parse_svn_unhandled(g):
         if e.errno != errno.EEXIST:
             raise
 
-    ver = -1
-    if CONFIG.has_option('core', 'version'):
-        ver = CONFIG.getint('core', 'version')
-
-    lastrev = None
-    if ver == VERSION:
-        FILES.read(FILES_PATH)
-        if CONFIG.has_option('core', 'lastrev'):
-            lastrev = CONFIG.getint('core', 'lastrev')
-
-    with open(g.path + '/svn/git-svn/unhandled.log', 'r') as f:
-        # Compile the regular expressions we'll be using here.
-        re_rev = re.compile("^r(\d+)$")
-        re_keywords = re.compile("^\s+[-+]file_prop: (\S+) svn:keywords ?(\S*)$")
-
-        rev = None
-        for line in f:
-            m = re_rev.match(line)
-            if m:
-                rev = m.group(1)
-                continue
-
-            if (lastrev >= int(rev)):
-                continue
-
-            m = re_keywords.match(line)
-            if m:
-                path = urllib.unquote(m.group(1))
-                keywords = set(urllib.unquote(m.group(2)).split(' '))
-                if not FILES.has_section(path):
-                    FILES.add_section(path)
-                FILES.set(path, rev, keywords)
-
-        lastrev = max(int(rev), lastrev)
-        CONFIG.set('core', 'lastrev', lastrev)
-        CONFIG.set('core', 'version', VERSION)
+    _do_parse_unhandled(os.path.join(g.path, 'svn'))
+    CONFIG.set('core', 'version', VERSION)
 
     with open(FILES_PATH, 'wb') as f:
         FILES.write(f)
@@ -128,51 +156,88 @@ def get_path_info(g, path):
 
     # tranlsate that into an svn revision id
     if not CONFIG.has_option('CommitToRev', commit):
-        CONFIG.set('CommitToRev',commit,g.git.svn('find-rev', commit))
+        file_rev = g.git.svn('find-rev', commit)
+        if not file_rev:
+            file_rev = "%iM" % find_last_svn_rev('HEAD')
+        CONFIG.set('CommitToRev', commit, file_rev)
         write_config = True
-    file_rev = CONFIG.get('CommitToRev', commit)
+    else:
+        file_rev = CONFIG.get('CommitToRev', commit)
+        if file_rev[-1] is 'M':
+            # Rewrite old nnnnnnM commits if they're now available in SVN
+            commit = g.commits('HEAD', path, 1)[0].id
+            file_rev2 = g.git.svn('find-rev', commit)
+            if file_rev2:
+                CONFIG.set('BlobToCommit', blob, commit)
+                CONFIG.set('CommitToRev', commit, file_rev2)
+                write_config = True
+                file_rev = file_rev2
 
     # get information about that revision
     info_dict = {}
-    if not CONFIG.has_option('RevInfo', file_rev):
-        for line in g.git.svn('info', path).split("\n"):
-            k, v = line.split(": ", 1)
-            if k == 'Last Changed Date':
-                info_dict['Date'] = v
-            elif k == 'Last Changed Author':
-                info_dict['Author'] = v
-        CONFIG.set('RevInfo', file_rev, info_dict)
-        write_config = True
-    else:
-        info = CONFIG.get('RevInfo', file_rev)
-        info_dict.update(info if type(info) is dict else eval(info))
-
-    if write_config:
-        with open(CONFIG_PATH, 'wb') as f:
-            CONFIG.write(f)
+    if file_rev:
+        if not CONFIG.has_option('RevInfo', file_rev):
+            for line in g.git.svn('info', path).split("\n"):
+                k, v = line.split(": ", 1)
+                if k == 'Last Changed Date':
+                    info_dict['Date'] = v
+                elif k == 'Last Changed Author':
+                    info_dict['Author'] = v
+            CONFIG.set('RevInfo', file_rev, info_dict)
+            write_config = True
+        else:
+            info = CONFIG.get('RevInfo', file_rev)
+            info_dict.update(info if type(info) is dict else eval(info))
+
+        if write_config:
+            with open(CONFIG_PATH, 'wb') as f:
+                CONFIG.write(f)
 
     info_dict['Revision'] = file_rev
     return info_dict
 
+def find_last_svn_rev(treeish, parent=0):
+    svnRev = g.git.svn('find-rev', "%s~%i" % (treeish, parent))
+    if svnRev:
+        return int(svnRev)
+    else:
+        return find_last_svn_rev(treeish, parent+1)
+
 # Do the work.
-def smudge(g, clean=False):
+def smudge(g, options):
+    read_file_data()
     parse_svn_unhandled(g)
-    rev_head = int(g.git.svn('find-rev', 'HEAD'))
+    rev_head = find_last_svn_rev('HEAD')
     url_base = g.git.svn('info', '--url')
 
     FILES.read(FILES_PATH)
     FILEINFO.read(FILEINFO_PATH)
 
+    ignores = []
+    with open(os.path.join(g.wd,'.git','info','exclude')) as f:
+        for line in f:
+            if line[0] != '#':
+                ignores.append(line.rstrip('\n'))
+
     paths = FILES.sections()
     paths.sort()
     for path in paths:
         if not os.path.exists(path):
             continue
 
-        kw_rev = max(filter(lambda x: x <= rev_head, map(int, FILES.options(path))))
+        ignore = False
+        for i in ignores:
+            if fnmatch(path, i):
+                ignore = True
+        if ignore:
+            continue
+        try:
+            kw_rev = max(filter(lambda x: x <= rev_head, map(int, FILES.options(path))))
+        except ValueError:
+            continue
 
         info_dict = {}
-        if not clean:
+        if not options.clean:
             info_dict.update(get_path_info(g, path))
             info_dict['URL'] = '/'.join([url_base, path])
             info_dict['Name'] = os.path.basename(path)
@@ -186,7 +251,7 @@ def smudge(g, clean=False):
         for k in keywords:
             for sk in svn_keywords:
                 if k in svn_keywords[sk]:
-                    if clean:
+                    if options.clean:
                         buf = re.sub(get_svn_keyword_re(sk), '$\\1$', buf)
                     elif sk == 'Id':
                         id_str = ' '.join([info_dict['Name'],
@@ -199,27 +264,46 @@ def smudge(g, clean=False):
 
         with open(os.path.join(g.wd, path), 'w') as f:
             f.write(buf)
+        if options.verbose:
+            print path + ' [' + ', '.join(keywords) + '] [len: ' + str(len(buf)) +']'
 
-        #print path + ' [' + ', '.join(keywords) + '] [len: ' + str(len(buf)) +']'
+if __name__ == '__main__':
 
-def clean(g):
-    smudge(g,True)
+    parser = OptionParser(version="%prog "+str(VERSION))
+    parser.set_defaults(clean=None)
+    parser.add_option("-s", "--smudge",
+                      action="store_false", dest="clean",
+                      help="Populate svn:keywords.")
+    parser.add_option("-c", "--clean",
+                      action="store_true", dest="clean",
+                      help="Return svn:keywords to pristene state.")
+    parser.add_option("-v", "--verbose",
+                      action="store_true", dest="verbose", default=False)
+    (options, args) = parser.parse_args()
+
+    if (options.clean is None):
+        parser.print_help()
+        exit(0)
+    else:
+        try:
+            g = git.Repo()
+        except git.errors.InvalidGitRepositoryError:
+            print "You are not in a git repository or working directory."
+            exit(1)
 
-if __name__ == '__main__':
-    try:
-        g = git.Repo()
-    except git.errors.InvalidGitRepositoryError:
-        print "You are not in a git repository or working directory."
-        exit(1)
-
-    CONFIG_PATH = os.path.join(gsk(g), 'conf.ini')
-    FILES_PATH = os.path.join(gsk(g), 'files.ini')
-    FILEINFO_PATH = os.path.join(gsk(g), 'fileinfo.ini')
-
-    CONFIG.read(CONFIG_PATH)
-    for section in ['core','CommitToRev','BlobToCommit', 'RevInfo']:
-        if not CONFIG.has_section(section):
-            CONFIG.add_section(section)
-
-    smudge(g)
-    #clean(g)
+        if g.bare:
+            print "This appears to be a bare git repository."
+            exit(1)
+
+        os.chdir(g.wd)
+
+        CONFIG_PATH = os.path.join(gsk(g), 'conf.ini')
+        FILES_PATH = os.path.join(gsk(g), 'files.ini')
+        FILEINFO_PATH = os.path.join(gsk(g), 'fileinfo.ini')
+
+        CONFIG.read(CONFIG_PATH)
+        for section in ['core','CommitToRev','BlobToCommit', 'RevInfo']:
+            if not CONFIG.has_section(section):
+                CONFIG.add_section(section)
+
+        smudge(g, options)