Import git show output easily
[stgit] / stgit / commands / common.py
index 10d0817..0d39148 100644 (file)
@@ -53,11 +53,20 @@ def git_id(crt_series, rev):
     repository = libstack.Repository.default()
     return git_commit(rev, repository, crt_series.get_name()).sha1
 
+def get_public_ref(branch_name):
+    """Return the public ref of the branch."""
+    public_ref = config.get('branch.%s.public' % branch_name)
+    if not public_ref:
+        public_ref = 'refs/heads/%s.public' % branch_name
+    return public_ref
+
 def git_commit(name, repository, branch_name = None):
     """Return the a Commit object if 'name' is a patch name or Git commit.
     The patch names allowed are in the form '<branch>:<patch>' and can
     be followed by standard symbols used by git rev-parse. If <patch>
-    is '{base}', it represents the bottom of the stack.
+    is '{base}', it represents the bottom of the stack. If <patch> is
+    {public}, it represents the public branch corresponding to the stack as
+    described in the 'publish' command.
     """
     # Try a [branch:]patch name first
     branch, patch = parse_rev(name)
@@ -69,6 +78,11 @@ def git_commit(name, repository, branch_name = None):
         base_id = repository.get_stack(branch).base.sha1
         return repository.rev_parse(base_id +
                                     strip_prefix('{base}', patch))
+    elif patch.startswith('{public}'):
+        public_ref = get_public_ref(branch)
+        return repository.rev_parse(public_ref +
+                                    strip_prefix('{public}', patch),
+                                    discard_stderr = True)
 
     # Other combination of branch and patch
     try:
@@ -83,6 +97,15 @@ def git_commit(name, repository, branch_name = None):
     except libgit.RepositoryException:
         raise CmdException('%s: Unknown patch or revision name' % name)
 
+def color_diff_flags():
+    """Return the git flags for coloured diff output if the configuration and
+    stdout allows."""
+    stdout_is_tty = (sys.stdout.isatty() and 'true') or 'false'
+    if config.get_colorbool('color.diff', stdout_is_tty) == 'true':
+        return ['--color']
+    else:
+        return []
+
 def check_local_changes():
     if git.local_changes():
         raise CmdException('local changes in the tree. Use "refresh" or'
@@ -96,8 +119,9 @@ def check_head_top_equal(crt_series):
 
 def check_conflicts():
     if git.get_conflicts():
-        raise CmdException('Unsolved conflicts. Please resolve them first'
-                           ' or revert the changes with "status --reset"')
+        raise CmdException('Unsolved conflicts. Please fix the conflicts'
+                           ' then use "git add --update <files>" or revert the'
+                           ' changes with "status --reset".')
 
 def print_crt_patch(crt_series, branch = None):
     if not branch:
@@ -299,7 +323,7 @@ def post_rebase(crt_series, applied, nopush, merged):
 #
 def __end_descr(line):
     return re.match('---\s*$', line) or re.match('diff -', line) or \
-            re.match('Index: ', line)
+            re.match('Index: ', line) or re.match('--- \w', line)
 
 def __split_descr_diff(string):
     """Return the description and the diff from the given string
@@ -331,6 +355,7 @@ def __parse_description(descr):
 
     lasthdr = 0
     end = len(descr_lines)
+    descr_strip = 0
 
     # Parse the patch header
     for pos in range(0, end):
@@ -350,12 +375,16 @@ def __parse_description(descr):
         if subject:
             break
         # get the subject
-        subject = descr_lines[pos]
+        subject = descr_lines[pos][descr_strip:]
+        if re.match('commit [\da-f]{40}$', subject):
+            # 'git show' output, look for the real subject
+            subject = ''
+            descr_strip = 4
         lasthdr = pos + 1
 
     # get the body
     if lasthdr < end:
-        body = reduce(lambda x, y: x + '\n' + y, descr_lines[lasthdr:], '')
+        body = '\n' + '\n'.join(l[descr_strip:] for l in descr_lines[lasthdr:])
 
     return (subject + body, authname, authemail, authdate)
 
@@ -381,7 +410,8 @@ def parse_mail(msg):
         authname = authemail = None
 
     # '\n\t' can be found on multi-line headers
-    descr = __decode_header(msg['subject']).replace('\n\t', ' ')
+    descr = __decode_header(msg['subject'])
+    descr = re.sub('\n[ \t]*', ' ', descr)
     authdate = msg['date']
 
     # remove the '[*PATCH*]' expression in the subject
@@ -438,6 +468,33 @@ def readonly_constant_property(f):
         return getattr(self, n)
     return property(new_f)
 
+def update_commit_data(cd, options):
+    """Return a new CommitData object updated according to the command line
+    options."""
+    # Set the commit message from commandline.
+    if options.message != None:
+        cd = cd.set_message(options.message)
+
+    # Modify author data.
+    cd = cd.set_author(options.author(cd.author))
+
+    # Add Signed-off-by: or similar.
+    if options.sign_str != None:
+        sign_str = options.sign_str
+    else:
+        sign_str = config.get("stgit.autosign")
+    if sign_str != None:
+        cd = cd.set_message(
+            add_sign_line(cd.message, sign_str,
+                          cd.committer.name, cd.committer.email))
+
+    # Let user edit the commit message manually, unless
+    # --save-template or --message was specified.
+    if not getattr(options, 'save_template', None) and not options.message:
+        cd = cd.set_message(edit_string(cd.message, '.stgit-new.txt'))
+
+    return cd
+
 class DirectoryException(StgException):
     pass