chiark / gitweb /
Execute the 'git ...' rather than 'git-...'
[stgit] / stgit / utils.py
index 18198c00831999e75a59962307a02beafe72e731..3a480c0e6d9d9a2c0323fca3aaa789fd96068f08 100644 (file)
@@ -1,8 +1,10 @@
 """Common utility functions
 """
 
-import errno, os, os.path, re, sys
+import errno, optparse, os, os.path, re, sys
+from stgit.exception import *
 from stgit.config import config
+from stgit.out import *
 
 __copyright__ = """
 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
@@ -27,6 +29,14 @@ def mkdir_file(filename, mode):
     create_dirs(os.path.dirname(filename))
     return file(filename, mode)
 
+def read_strings(filename):
+    """Reads the lines from a file
+    """
+    f = file(filename, 'r')
+    lines = [line.strip() for line in f.readlines()]
+    f.close()
+    return lines
+
 def read_string(filename, multiline = False):
     """Reads the first line from a file
     """
@@ -38,6 +48,13 @@ def read_string(filename, multiline = False):
     f.close()
     return result
 
+def write_strings(filename, lines):
+    """Write 'lines' sequence to file
+    """
+    f = file(filename, 'w+')
+    f.writelines([line + '\n' for line in lines])
+    f.close()
+
 def write_string(filename, line, multiline = False):
     """Writes 'line' to file and truncates it
     """
@@ -150,7 +167,7 @@ def rename(basedir, file1, file2):
         # file1's parent dir may not be empty after move
         pass
 
-class EditorException(Exception):
+class EditorException(StgException):
     pass
 
 def call_editor(filename):
@@ -166,34 +183,76 @@ def call_editor(filename):
         editor = 'vi'
     editor += ' %s' % filename
 
-    print 'Invoking the editor: "%s"...' % editor,
-    sys.stdout.flush()
+    out.start('Invoking the editor: "%s"' % editor)
     err = os.system(editor)
     if err:
         raise EditorException, 'editor failed, exit code: %d' % err
-    print 'done'
+    out.done()
 
 def patch_name_from_msg(msg):
     """Return a string to be used as a patch name. This is generated
-    from the first 30 characters of the top line of the string passed
-    as argument."""
+    from the top line of the string passed as argument."""
     if not msg:
         return None
 
-    subject_line = msg[:30].lstrip().split('\n', 1)[0].lower()
-    return re.sub('[\W]+', '-', subject_line).strip('-')
+    name_len = config.get('stgit.namelength')
+    if not name_len:
+        name_len = 30
+
+    subject_line = msg.split('\n', 1)[0].lstrip().lower()
+    return re.sub('[\W]+', '-', subject_line).strip('-')[:name_len]
 
-def make_patch_name(msg, unacceptable, default_name = 'patch',
-                    alternative = True):
+def make_patch_name(msg, unacceptable, default_name = 'patch'):
     """Return a patch name generated from the given commit message,
     guaranteed to make unacceptable(name) be false. If the commit
     message is empty, base the name on default_name instead."""
     patchname = patch_name_from_msg(msg)
     if not patchname:
         patchname = default_name
-    if alternative and unacceptable(patchname):
+    if unacceptable(patchname):
         suffix = 0
         while unacceptable('%s-%d' % (patchname, suffix)):
             suffix += 1
         patchname = '%s-%d' % (patchname, suffix)
     return patchname
+
+# any and all functions are builtin in Python 2.5 and higher, but not
+# in 2.4.
+if not 'any' in dir(__builtins__):
+    def any(bools):
+        for b in bools:
+            if b:
+                return True
+        return False
+if not 'all' in dir(__builtins__):
+    def all(bools):
+        for b in bools:
+            if not b:
+                return False
+        return True
+
+def make_sign_options():
+    def callback(option, opt_str, value, parser, sign_str):
+        if parser.values.sign_str not in [None, sign_str]:
+            raise optparse.OptionValueError(
+                '--ack and --sign were both specified')
+        parser.values.sign_str = sign_str
+    return [optparse.make_option('--sign', action = 'callback',
+                                 callback = callback, dest = 'sign_str',
+                                 callback_args = ('Signed-off-by',),
+                                 help = 'add Signed-off-by line'),
+            optparse.make_option('--ack', action = 'callback',
+                                 callback = callback, dest = 'sign_str',
+                                 callback_args = ('Acked-by',),
+                                 help = 'add Acked-by line')]
+
+def add_sign_line(desc, sign_str, name, email):
+    if not sign_str:
+        return desc
+    sign_str = '%s: %s <%s>' % (sign_str, name, email)
+    if sign_str in desc:
+        return desc
+    desc = desc.rstrip()
+    if not any(s in desc for s in ['\nSigned-off-by:', '\nAcked-by:']):
+        desc = desc + '\n'
+    return '%s\n%s\n' % (desc, sign_str)