Subject: [PATCH 1/3] Ask git for author and committer name

From: Karl Hasselström <[email-masked]>
Date: Sun, 12 Nov 2006 00:30:46 +0100

From: Karl Hasselström <[email-masked]>

Consistently do the following to get hold of default user and
committer:

  1. Use the value specified on the command line, if any.

  1. Otherwise, use the value from stgitrc, if available.

  2. Otherwise, ask git for the value. git will produce the value from
     on of its config files, from environment variables, or make it
     up. It might be asking the spirits of the dead for all we care.

Signed-off-by: Karl Hasselström <[email-masked]>
---

 stgit/commands/mail.py |   19 ++------------
 stgit/git.py           |   63 ++++++++++++++++++++++++++++++++++++++++++++++++
 stgit/stack.py         |   32 ++++--------------------
 3 files changed, 70 insertions(+), 44 deletions(-)

diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index 154df9c..78abfa4 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -127,17 +127,6 @@ options = [make_option('-a', '--all',
                        action = 'store_true')]

-def __get_maintainer():
-    """Return the 'authname <authemail>' string as read from the
-    configuration file
-    """
-    if config.has_option('stgit', 'authname') \
-           and config.has_option('stgit', 'authemail'):
-        return '%s <%s>' % (config.get('stgit', 'authname'),
-                            config.get('stgit', 'authemail'))
-    else:
-        return None
-
 def __parse_addresses(addresses):
     """Return a two elements tuple: (from, [to])
     """
@@ -301,9 +290,7 @@ def edit_message(msg):
 def __build_cover(tmpl, total_nr, msg_id, options):
     """Build the cover message (series description) to be sent via SMTP
     """
-    maintainer = __get_maintainer()
-    if not maintainer:
-        maintainer = ''
+    maintainer = git.user()
 
     if options.version:
         version_str = ' %s' % options.version
@@ -370,9 +357,7 @@ def __build_message(tmpl, patch, patch_n
     short_descr = descr_lines[0].rstrip()
     long_descr = '\n'.join(descr_lines[1:]).lstrip()
 
-    maintainer = __get_maintainer()
-    if not maintainer:
-        maintainer = '%s <%s>' % (p.get_commname(), p.get_commemail())
+    maintainer = git.user()
 
     if options.version:
         version_str = ' %s' % options.version
diff --git a/stgit/git.py b/stgit/git.py
index 8d88769..a6e1a63 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -33,6 +33,35 @@ class GitException(Exception):
 #
 # Classes
 #
+
+class Person:
+    """An author, committer, etc."""
+    def __init__(self, name = None, email = None, date = None,
+                 desc = None):
+        if name or email or date:
+            assert not desc
+            self.name = name
+            self.email = email
+            self.date = date
+        elif desc:
+            assert not (name or email or date)
+            def parse_desc(s):
+                m = re.match(r'^(.+)<(.+)>(.*)$', s)
+                assert m
+                return [x.strip() or None for x in m.groups()]
+            self.name, self.email, self.date = parse_desc(desc)
+    def set_name(self, val):
+        if val:
+            self.name = val
+    def set_email(self, val):
+        if val:
+            self.email = val
+    def __str__(self):
+        if self.name and self.email:
+            return '%s <%s>' % (self.name, self.email)
+        else:
+            raise Exception, 'not enough identity data'
+
 class Commit:
     """Handle the commit objects
     """
@@ -402,6 +431,40 @@ def rm(files, force = False):
         if files:
             __run('git-update-index --force-remove --', files)
 
+def var(key):
+    """Ask git-var for the value of a variable."""
+    return _output_one_line(['git-var', key])
+
+def repo_config(key):
+    """Ask git-repo-config for the value of a variable."""
+    return _output_one_line(['git-repo-config', key])
+
+__cached_git_persons = {}
+def __git_person(p):
+    if not p in __cached_git_persons:
+        __cached_git_persons[p] = {
+            'author': lambda: Person(desc = var('GIT_AUTHOR_IDENT')),
+            'committer': lambda: Person(desc = var('GIT_COMMITTER_IDENT')),
+            'user': lambda: Person(repo_config('user.name'),
+                                   repo_config('user.email')),
+            }[p]()
+    return __cached_git_persons[p]
+
+__cached_stgit_persons = {}
+def __stgit_person(p, name_key, email_key):
+    if not p in __cached_stgit_persons:
+        person = __git_person(p)
+        if config.has_option('stgit', name_key):
+            person.set_name(config.get('stgit', name_key))
+        if config.has_option('stgit', email_key):
+            person.set_email(config.get('stgit', email_key))
+        __cached_stgit_persons[p] = person
+    return __cached_stgit_persons[p]
+
+def author(): return __stgit_person('author', 'authname', 'authemail')
+def committer(): return __stgit_person('committer', 'commname', 'commemail')
+def user(): return __stgit_person('user', 'authname', 'authemail')
+
 def update_cache(files = None, force = False):
     """Update the cache information for the given files
     """
diff --git a/stgit/stack.py b/stgit/stack.py
index a477e7d..fee1316 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -238,53 +238,31 @@ class Patch:
         return self.__get_field('authname')
 
     def set_authname(self, name):
-        if not name:
-            if config.has_option('stgit', 'authname'):
-                name = config.get('stgit', 'authname')
-            elif 'GIT_AUTHOR_NAME' in os.environ:
-                name = os.environ['GIT_AUTHOR_NAME']
-        self.__set_field('authname', name)
+        self.__set_field('authname', name or git.author().name)
 
     def get_authemail(self):
         return self.__get_field('authemail')
 
     def set_authemail(self, address):
-        if not address:
-            if config.has_option('stgit', 'authemail'):
-                address = config.get('stgit', 'authemail')
-            elif 'GIT_AUTHOR_EMAIL' in os.environ:
-                address = os.environ['GIT_AUTHOR_EMAIL']
-        self.__set_field('authemail', address)
+        self.__set_field('authemail', address or git.author().email)
 
     def get_authdate(self):
         return self.__get_field('authdate')
 
     def set_authdate(self, date):
-        if not date and 'GIT_AUTHOR_DATE' in os.environ:
-            date = os.environ['GIT_AUTHOR_DATE']
-        self.__set_field('authdate', date)
+        self.__set_field('authdate', date or git.author().date)
 
     def get_commname(self):
         return self.__get_field('commname')
 
     def set_commname(self, name):
-        if not name:
-            if config.has_option('stgit', 'commname'):
-                name = config.get('stgit', 'commname')
-            elif 'GIT_COMMITTER_NAME' in os.environ:
-                name = os.environ['GIT_COMMITTER_NAME']
-        self.__set_field('commname', name)
+        self.__set_field('commname', name or git.committer().name)
 
     def get_commemail(self):
         return self.__get_field('commemail')
 
     def set_commemail(self, address):
-        if not address:
-            if config.has_option('stgit', 'commemail'):
-                address = config.get('stgit', 'commemail')
-            elif 'GIT_COMMITTER_EMAIL' in os.environ:
-                address = os.environ['GIT_COMMITTER_EMAIL']
-        self.__set_field('commemail', address)
+        self.__set_field('commemail', address or git.committer().email)
 
     def get_log(self):