Avoid allowing --undo if it doesn't change anything
[stgit] / stgit / stack.py
index 1e45e72..55c49a8 100644 (file)
@@ -92,7 +92,9 @@ def edit_file(series, string, comment, show_patch = True):
     f.close()
 
     # the editor
-    if 'EDITOR' in os.environ:
+    if config.has_option('stgit', 'editor'):
+        editor = config.get('stgit', 'editor')
+    elif 'EDITOR' in os.environ:
         editor = os.environ['EDITOR']
     else:
         editor = 'vi'
@@ -168,7 +170,11 @@ class Patch:
 
     def set_bottom(self, string, backup = False):
         if backup:
-            self.__set_field('bottom.old', self.__get_field('bottom'))
+            curr = self.__get_field('bottom')
+            if curr != string:
+                self.__set_field('bottom.old', curr)
+            else:
+                self.__set_field('bottom.old', None)
         self.__set_field('bottom', string)
 
     def get_top(self):
@@ -176,7 +182,11 @@ class Patch:
 
     def set_top(self, string, backup = False):
         if backup:
-            self.__set_field('top.old', self.__get_field('top'))
+            curr = self.__get_field('top')
+            if curr != string:
+                self.__set_field('top.old', curr)
+            else:
+                self.__set_field('top.old', None)
         self.__set_field('top', string)
 
     def restore_old_boundaries(self):
@@ -186,8 +196,9 @@ class Patch:
         if top and bottom:
             self.__set_field('bottom', bottom)
             self.__set_field('top', top)
+            return True
         else:
-            raise StackException, 'No patch undo information'
+            return False
 
     def get_description(self):
         return self.__get_field('description', True)
@@ -255,6 +266,11 @@ class Series:
             self.__unapplied_file = os.path.join(self.__patch_dir, 'unapplied')
             self.__current_file = os.path.join(self.__patch_dir, 'current')
 
+    def get_branch(self):
+        """Return the branch name for the Series object
+        """
+        return self.__name
+
     def __set_current(self, name):
         """Sets the topmost patch
         """
@@ -406,7 +422,9 @@ class Series:
 
         return commit_id
 
-    def new_patch(self, name, message = None, edit = False, show_patch = False,
+    def new_patch(self, name, message = None, can_edit = True,
+                  unapplied = False, show_patch = False,
+                  top = None, bottom = None,
                   author_name = None, author_email = None, author_date = None,
                   committer_name = None, committer_email = None):
         """Creates a new patch
@@ -414,7 +432,7 @@ class Series:
         if self.__patch_applied(name) or self.__patch_unapplied(name):
             raise StackException, 'Patch "%s" already exists' % name
 
-        if not message:
+        if not message and can_edit:
             descr = edit_file(self, None, \
                               'Please enter the description for patch "%s" ' \
                               'above.' % name, show_patch)
@@ -427,8 +445,16 @@ class Series:
 
         patch = Patch(name, self.__patch_dir)
         patch.create()
-        patch.set_bottom(head)
-        patch.set_top(head)
+
+        if bottom:
+            patch.set_bottom(bottom)
+        else:
+            patch.set_bottom(head)
+        if top:
+            patch.set_top(top)
+        else:
+            patch.set_top(head)
+
         patch.set_description(descr)
         patch.set_authname(author_name)
         patch.set_authemail(author_email)
@@ -436,8 +462,15 @@ class Series:
         patch.set_commname(committer_name)
         patch.set_commemail(committer_email)
 
-        append_string(self.__applied_file, patch.get_name())
-        self.__set_current(name)
+        if unapplied:
+            patches = [patch.get_name()] + self.get_unapplied()
+
+            f = file(self.__unapplied_file, 'w+')
+            f.writelines([line + '\n' for line in patches])
+            f.close()
+        else:
+            append_string(self.__applied_file, patch.get_name())
+            self.__set_current(name)
 
     def delete_patch(self, name):
         """Deletes a patch
@@ -460,6 +493,53 @@ class Series:
         f.writelines([line + '\n' for line in unapplied])
         f.close()
 
+    def forward_patches(self, names):
+        """Try to fast-forward an array of patches.
+
+        On return, patches in names[0:returned_value] have been pushed on the
+        stack. Apply the rest with push_patch
+        """
+        unapplied = self.get_unapplied()
+        self.__begin_stack_check()
+
+        forwarded = 0
+        top = git.get_head()
+
+        for name in names:
+            assert(name in unapplied)
+
+            patch = Patch(name, self.__patch_dir)
+
+            head = top
+            bottom = patch.get_bottom()
+            top = patch.get_top()
+
+            # top != bottom always since we have a commit for each patch
+            if head == bottom:
+                # reset the backup information
+                patch.set_bottom(bottom, backup = True)
+                patch.set_top(top, backup = True)
+
+            else:
+                top = head
+                # stop the fast-forwarding, must do a real merge
+                break
+
+            forwarded+=1
+            unapplied.remove(name)
+
+        git.switch(top)
+
+        append_strings(self.__applied_file, names[0:forwarded])
+
+        f = file(self.__unapplied_file, 'w+')
+        f.writelines([line + '\n' for line in unapplied])
+        f.close()
+
+        self.__set_current(name)
+
+        return forwarded
+
     def push_patch(self, name):
         """Pushes a patch on the stack
         """
@@ -522,7 +602,7 @@ class Series:
         patch = Patch(name, self.__patch_dir)
         git.reset()
         self.pop_patch(name)
-        patch.restore_old_boundaries()
+        return patch.restore_old_boundaries()
 
     def pop_patch(self, name):
         """Pops the top patch from the stack