chiark / gitweb /
Do not raise an exception if no FETCH_HEAD
[stgit] / stgit / stack.py
index 37ffb6a6ce6e2e6101b055c83203cb92651a96af..ad1ed2b67ed76b1075a9c11f3548242e7163d9c3 100644 (file)
@@ -21,15 +21,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 import sys, os, re
 from email.Utils import formatdate
 
+from stgit.exception import *
 from stgit.utils import *
 from stgit.out import *
+from stgit.run import *
 from stgit import git, basedir, templates
 from stgit.config import config
 from shutil import copyfile
 
 
 # stack exception class
-class StackException(Exception):
+class StackException(StgException):
     pass
 
 class FilterUntil:
@@ -143,7 +145,7 @@ class StgitObject:
         elif os.path.isfile(fname):
             os.remove(fname)
 
-    
+
 class Patch(StgitObject):
     """Basic patch implementation
     """
@@ -508,11 +510,17 @@ class Series(PatchSet):
             raise StackException, 'Branch "%s" not initialised' % self.get_name()
         return read_strings(self.__applied_file)
 
+    def set_applied(self, applied):
+        write_strings(self.__applied_file, applied)
+
     def get_unapplied(self):
         if not os.path.isfile(self.__unapplied_file):
             raise StackException, 'Branch "%s" not initialised' % self.get_name()
         return read_strings(self.__unapplied_file)
 
+    def set_unapplied(self, unapplied):
+        write_strings(self.__unapplied_file, unapplied)
+
     def get_hidden(self):
         if not os.path.isfile(self.__hidden_file):
             return []
@@ -615,7 +623,6 @@ class Series(PatchSet):
 
         self.create_empty_field('applied')
         self.create_empty_field('unapplied')
-        self._set_field('orig-base', git.get_head())
 
         config.set(self.format_version_key(), str(FORMAT_VERSION))
 
@@ -754,7 +761,7 @@ class Series(PatchSet):
                       author_date = None,
                       committer_name = None, committer_email = None,
                       backup = False, sign_str = None, log = 'refresh',
-                      notes = None):
+                      notes = None, bottom = None):
         """Generates a new commit for the topmost patch
         """
         patch = self.get_current_patch()
@@ -788,7 +795,8 @@ class Series(PatchSet):
 
         descr = add_sign_line(descr, sign_str, committer_name, committer_email)
 
-        bottom = patch.get_bottom()
+        if not bottom:
+            bottom = patch.get_bottom()
 
         commit_id = git.commit(files = files,
                                message = descr, parents = [bottom],
@@ -839,10 +847,17 @@ class Series(PatchSet):
                   top = None, bottom = None, commit = True,
                   author_name = None, author_email = None, author_date = None,
                   committer_name = None, committer_email = None,
-                  before_existing = False):
-        """Creates a new patch
+                  before_existing = False, sign_str = None):
+        """Creates a new patch, either pointing to an existing commit object,
+        or by creating a new commit object.
         """
 
+        assert commit or (top and bottom)
+        assert not before_existing or (top and bottom)
+        assert not (commit and before_existing)
+        assert (top and bottom) or (not top and not bottom)
+        assert commit or (not top or (bottom == git.get_commit(top).get_parent()))
+
         if name != None:
             self.__patch_name_valid(name)
             if self.patch_exists(name):
@@ -850,13 +865,17 @@ class Series(PatchSet):
 
         # TODO: move this out of the stgit.stack module, it is really
         # for higher level commands to handle the user interaction
+        def sign(msg):
+            return add_sign_line(msg, sign_str,
+                                 committer_name or git.committer().name,
+                                 committer_email or git.committer().email)
         if not message and can_edit:
             descr = edit_file(
-                self, None,
+                self, sign(''),
                 'Please enter the description for the patch above.',
                 show_patch)
         else:
-            descr = message
+            descr = sign(message)
 
         head = git.get_head()
 
@@ -866,13 +885,6 @@ class Series(PatchSet):
         patch = self.get_patch(name)
         patch.create()
 
-        if not bottom:
-            bottom = head
-        if not top:
-            top = head
-
-        patch.set_bottom(bottom)
-        patch.set_top(top)
         patch.set_description(descr)
         patch.set_authname(author_name)
         patch.set_authemail(author_email)
@@ -882,9 +894,6 @@ class Series(PatchSet):
 
         if before_existing:
             insert_string(self.__applied_file, patch.get_name())
-            # no need to commit anything as the object is already
-            # present (mainly used by 'uncommit')
-            commit = False
         elif unapplied:
             patches = [patch.get_name()] + self.get_unapplied()
             write_strings(self.__unapplied_file, patches)
@@ -894,10 +903,15 @@ class Series(PatchSet):
             set_head = True
 
         if commit:
+            if top:
+                top_commit = git.get_commit(top)
+            else:
+                bottom = head
+                top_commit = git.get_commit(head)
+
             # create a commit for the patch (may be empty if top == bottom);
             # only commit on top of the current branch
             assert(unapplied or bottom == head)
-            top_commit = git.get_commit(top)
             commit_id = git.commit(message = descr, parents = [bottom],
                                    cache_update = False,
                                    tree_id = top_commit.get_tree(),
@@ -908,7 +922,12 @@ class Series(PatchSet):
                                    committer_name = committer_name,
                                    committer_email = committer_email)
             # set the patch top to the new commit
+            patch.set_bottom(bottom)
             patch.set_top(commit_id)
+        else:
+            assert top != bottom
+            patch.set_bottom(bottom)
+            patch.set_top(top)
 
         self.log_patch(patch, 'new')
 
@@ -1036,20 +1055,15 @@ class Series(PatchSet):
         unapplied = self.get_unapplied()
         assert(name in unapplied)
 
-        patch = self.get_patch(name)
+        patch = self.get_patch(name)
         head = git.get_head()
 
-        # The top is updated by refresh_patch since we need an empty
-        # commit
-        patch.set_bottom(head, backup = True)
-        patch.set_top(head, backup = True)
-
         append_string(self.__applied_file, name)
 
         unapplied.remove(name)
         write_strings(self.__unapplied_file, unapplied)
 
-        self.refresh_patch(cache_update = False, log = 'push(m)')
+        self.refresh_patch(bottom = head, cache_update = False, log = 'push(m)')
 
     def push_patch(self, name):
         """Pushes a patch on the stack
@@ -1081,11 +1095,6 @@ class Series(PatchSet):
         ex = None
         modified = False
 
-        # new patch needs to be refreshed.
-        # The current patch is empty after merge.
-        patch.set_bottom(head, backup = True)
-        patch.set_top(head, backup = True)
-
         # Try the fast applying first. If this fails, fall back to the
         # three-way merge
         if not git.apply_diff(bottom, top):
@@ -1113,13 +1122,14 @@ class Series(PatchSet):
                 log = 'push(m)'
             else:
                 log = 'push'
-            self.refresh_patch(cache_update = False, log = log)
+            self.refresh_patch(bottom = head, cache_update = False, log = log)
         else:
             # we store the correctly merged files only for
             # tracking the conflict history. Note that the
             # git.merge() operations should always leave the index
             # in a valid state (i.e. only stage 0 files)
-            self.refresh_patch(cache_update = False, log = 'push(c)')
+            self.refresh_patch(bottom = head, cache_update = False,
+                               log = 'push(c)')
             raise StackException, str(ex)
 
         return modified
@@ -1157,7 +1167,8 @@ class Series(PatchSet):
         patch = self.get_patch(name)
 
         if git.get_head_file() == self.get_name():
-            if keep and not git.apply_diff(git.get_head(), patch.get_bottom()):
+            if keep and not git.apply_diff(git.get_head(), patch.get_bottom(),
+                                           check_index = False):
                 raise StackException(
                     'Failed to pop patches while preserving the local changes')
             git.switch(patch.get_bottom(), keep)