threads / patch / 11285

patch, 5 partsMore experimental patches

Subject: [StGit PATCH 0/5] More experimental patches

## tl;dr

6 messages between Dec 14, 2007 and Dec 14, 2007. Diffs are folded; open one to read it.

replies: 5people: 1as markdown or json

Karl Hasselström· Dec 14, 2007, 06:32 UTC · lore
Also available from
  git://repo.or.cz/stgit/kha.git experimental

However, the experimentalness is getting decidedly low. And now that we just had a release, I have just one more thing I intend to take a look at -- subdirectory safeness -- before I'm going to transfer all the "experimental" patches to "safe", wholesale.

So if anyone knows of problems that would make this a bad idea, please say so.

---
Karl Hasselström (5):
      Make "stg commit" fancier
      Convert "stg commit" to new infrastructure
      Set exit code to 3 on merge conflict
      stg coalesce: Support --file and --save-template
      Expose transaction abort function
 stgit/commands/coalesce.py |   32 ++++++++-----
 stgit/commands/commit.py   |  111 ++++++++++++++++++++++++++++----------------
 stgit/commands/goto.py     |    2 -
 stgit/commands/uncommit.py |    2 -
 stgit/lib/transaction.py   |   24 ++++++++--
 stgit/main.py              |    4 +-
 stgit/utils.py             |    1 
 t/t1300-uncommit.sh        |   12 ++---
 8 files changed, 121 insertions(+), 67 deletions(-)
-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle
Karl Hasselström· Dec 14, 2007, 06:32 UTC · re: Karl Hasselström · lore

[StGit PATCH 1/5] Expose transaction abort function

Users of stack transactions may call abort() instead of run(), if they wish to roll back the transaction instead of committing it.

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/lib/transaction.py |    9 ++++++---
 1 files changed, 6 insertions(+), 3 deletions(-)
Show changes to stgit/lib/transaction.py +6 −3
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index 77333b3..663d393 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -77,6 +77,10 @@ class StackTransaction(object):
             return self.__patches[self.__applied[-1]]
         else:
             return self.__stack.base
+    def abort(self, iw = None):
+        # The only state we need to restore is index+worktree.
+        if iw:
+            self.__checkout(self.__stack.head.data.tree, iw)
     def run(self, iw = None):
         self.__check_consistency()
         new_head = self.__head
@@ -85,9 +89,8 @@ class StackTransaction(object):
         try:
             self.__checkout(new_head.data.tree, iw)
         except git.CheckoutException:
-            # We have to abort the transaction. The only state we need
-            # to restore is index+worktree.
-            self.__checkout(self.__stack.head.data.tree, iw)
+            # We have to abort the transaction.
+            self.abort(iw)
             self.__abort()
         self.__stack.set_head(new_head, self.__msg)
 
Karl Hasselström· Dec 14, 2007, 06:32 UTC · re: Karl Hasselström · lore

[StGit PATCH 2/5] stg coalesce: Support --file and --save-template

--save-template was a bit tricky, because we want that
  * if we reached the stage where the message is needed without
    conflicts, the message should be written and no other side effects
    should occur; but
  * if we run into conflicts before reaching that point, behave just
    as if --save-template was not given.
This makes this script
  stg coalesce --save-template <patches>
  if template was saved:
    let user edit template
    if user didn't abort:
      stg coalesce --file <patches>
equivalent to
  stg coalesce <patches>

with the added benefit that the user can abort the whole thing without visible side effects.

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/commands/coalesce.py |   28 +++++++++++++++++++---------
 1 files changed, 19 insertions(+), 9 deletions(-)
Show changes to stgit/commands/coalesce.py +19 −9
diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
index e3e1629..2330231 100644
--- a/stgit/commands/coalesce.py
+++ b/stgit/commands/coalesce.py
@@ -34,11 +34,13 @@ you specify, you will have to resolve them manually just as if you had
 done a sequence of pushes and pops yourself."""
 
 directory = common.DirectoryHasRepositoryLib()
-options = [make_option('-n', '--name', help = 'name of coalesced patch'),
-           make_option('-m', '--message',
-                       help = 'commit message of coalesced patch')]
+options = [make_option('-n', '--name', help = 'name of coalesced patch')
+           ] + utils.make_message_options()
 
-def _coalesce_patches(trans, patches, msg):
+class SaveTemplateDone(Exception):
+    pass
+
+def _coalesce_patches(trans, patches, msg, save_template):
     cd = trans.patches[patches[0]].data
     cd = git.Commitdata(tree = cd.tree, parents = cd.parents)
     for pn in patches[1:]:
@@ -53,12 +55,16 @@ def _coalesce_patches(trans, patches, msg):
         msg = '\n\n'.join('%s\n\n%s' % (pn.ljust(70, '-'),
                                         trans.patches[pn].data.message)
                           for pn in patches)
-        msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
+        if save_template:
+            save_template(msg)
+            raise SaveTemplateDone()
+        else:
+            msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
     cd = cd.set_message(msg)
 
     return cd
 
-def _coalesce(stack, iw, name, msg, patches):
+def _coalesce(stack, iw, name, msg, save_template, patches):
 
     # If a name was supplied on the command line, make sure it's OK.
     def bad_name(pn):
@@ -75,8 +81,8 @@ def _coalesce(stack, iw, name, msg, patches):
 
     trans = transaction.StackTransaction(stack, 'stg coalesce')
     push_new_patch = bool(set(patches) & set(trans.applied))
-    new_commit_data = _coalesce_patches(trans, patches, msg)
     try:
+        new_commit_data = _coalesce_patches(trans, patches, msg, save_template)
         if new_commit_data:
             # We were able to construct the coalesced commit
             # automatically. So just delete its constituent patches.
@@ -88,7 +94,8 @@ def _coalesce(stack, iw, name, msg, patches):
             to_push = trans.pop_patches(lambda pn: pn in patches)
             for pn in patches:
                 trans.push_patch(pn, iw)
-            new_commit_data = _coalesce_patches(trans, patches, msg)
+            new_commit_data = _coalesce_patches(trans, patches, msg,
+                                                save_template)
             assert not trans.delete_patches(lambda pn: pn in patches)
         make_coalesced_patch(trans, new_commit_data)
 
@@ -98,6 +105,9 @@ def _coalesce(stack, iw, name, msg, patches):
             trans.push_patch(get_name(new_commit_data), iw)
         for pn in to_push:
             trans.push_patch(pn, iw)
+    except SaveTemplateDone:
+        trans.abort(iw)
+        return
     except transaction.TransactionHalted:
         pass
     trans.run(iw)
@@ -109,4 +119,4 @@ def func(parser, options, args):
     if len(patches) < 2:
         raise common.CmdException('Need at least two patches')
     _coalesce(stack, stack.repository.default_iw(),
-              options.name, options.message, patches)
+              options.name, options.message, options.save_template, patches)
Karl Hasselström· Dec 14, 2007, 06:32 UTC · re: Karl Hasselström · lore

[StGit PATCH 3/5] Set exit code to 3 on merge conflict

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/commands/coalesce.py |    6 +++---
 stgit/commands/goto.py     |    2 +-
 stgit/lib/transaction.py   |    7 ++++++-
 stgit/main.py              |    4 ++--
 stgit/utils.py             |    1 +
 5 files changed, 13 insertions(+), 7 deletions(-)
Show changes to 5 files +13 −7

stgit/commands/coalesce.py, stgit/commands/goto.py, stgit/lib/transaction.py, stgit/main.py, stgit/utils.py

diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
index 2330231..d2cba3e 100644
--- a/stgit/commands/coalesce.py
+++ b/stgit/commands/coalesce.py
@@ -110,7 +110,7 @@ def _coalesce(stack, iw, name, msg, save_template, patches):
         return
     except transaction.TransactionHalted:
         pass
-    trans.run(iw)
+    return trans.run(iw)
 
 def func(parser, options, args):
     stack = directory.repository.current_stack
@@ -118,5 +118,5 @@ def func(parser, options, args):
                                           + list(stack.patchorder.unapplied)))
     if len(patches) < 2:
         raise common.CmdException('Need at least two patches')
-    _coalesce(stack, stack.repository.default_iw(),
-              options.name, options.message, options.save_template, patches)
+    return _coalesce(stack, stack.repository.default_iw(), options.name,
+                     options.message, options.save_template, patches)
diff --git a/stgit/commands/goto.py b/stgit/commands/goto.py
index d78929d..763a8af 100644
--- a/stgit/commands/goto.py
+++ b/stgit/commands/goto.py
@@ -48,4 +48,4 @@ def func(parser, options, args):
             pass
     else:
         raise common.CmdException('Patch "%s" does not exist' % patch)
-    trans.run(iw)
+    return trans.run(iw)
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index 663d393..0ca647e 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -1,4 +1,4 @@
-from stgit import exception
+from stgit import exception, utils
 from stgit.out import *
 from stgit.lib import git
 
@@ -111,6 +111,11 @@ class StackTransaction(object):
         self.__stack.patchorder.applied = self.__applied
         self.__stack.patchorder.unapplied = self.__unapplied
 
+        if self.__error:
+            return utils.STGIT_CONFLICT
+        else:
+            return utils.STGIT_SUCCESS
+
     def __halt(self, msg):
         self.__error = msg
         raise TransactionHalted(msg)
diff --git a/stgit/main.py b/stgit/main.py
index a95eeb9..2577693 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -273,7 +273,7 @@ def main():
             else:
                 command.crt_series = Series()
 
-        command.func(parser, options, args)
+        ret = command.func(parser, options, args)
     except (StgException, IOError, ParsingError, NoSectionError), err:
         out.error(str(err), title = '%s %s' % (prog, cmd))
         if debug_level > 0:
@@ -283,4 +283,4 @@ def main():
     except KeyboardInterrupt:
         sys.exit(utils.STGIT_GENERAL_ERROR)
 
-    sys.exit(utils.STGIT_SUCCESS)
+    sys.exit(ret or utils.STGIT_SUCCESS)
diff --git a/stgit/utils.py b/stgit/utils.py
index 6568da5..2ff1d74 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -317,6 +317,7 @@ def make_message_options():
 STGIT_SUCCESS = 0        # everything's OK
 STGIT_GENERAL_ERROR = 1  # seems to be non-command-specific error
 STGIT_COMMAND_ERROR = 2  # seems to be a command that failed
+STGIT_CONFLICT = 3       # merge conflict, otherwise OK
 
 def strip_leading(prefix, s):
     """Strip leading prefix from a string. Blow up if the prefix isn't
Karl Hasselström· Dec 14, 2007, 06:32 UTC · re: Karl Hasselström · lore

[StGit PATCH 4/5] Convert "stg commit" to new infrastructure

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/commands/commit.py |   42 ++++++++++++++----------------------------
 stgit/lib/transaction.py |    7 ++++++-
 2 files changed, 20 insertions(+), 29 deletions(-)
Show changes to 2 files +20 −29

stgit/commands/commit.py, stgit/lib/transaction.py

diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py
index e56f5a0..f822181 100644
--- a/stgit/commands/commit.py
+++ b/stgit/commands/commit.py
@@ -15,13 +15,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import sys, os
-from optparse import OptionParser, make_option
-
-from stgit.commands.common import *
-from stgit.utils import *
+from stgit.commands import common
+from stgit.lib import transaction
 from stgit.out import *
-from stgit import stack, git
 
 help = 'permanently store the applied patches into stack base'
 usage = """%prog [options]
@@ -32,7 +28,7 @@ remove them from the series while advancing the base.
 Use this command only if you want to permanently store the applied
 patches and no longer manage them with StGIT."""
 
-directory = DirectoryGotoToplevel()
+directory = common.DirectoryHasRepositoryLib()
 options = []
 
 
@@ -43,25 +39,15 @@ def func(parser, options, args):
     if len(args) != 0:
         parser.error('incorrect number of arguments')
 
-    check_local_changes()
-    check_conflicts()
-    check_head_top_equal(crt_series)
-
-    applied = crt_series.get_applied()
-    if not applied:
-        raise CmdException, 'No patches applied'
-
-    if crt_series.get_protected():
-        raise CmdException, 'This branch is protected.  Commit is not permitted'
-
-    crt_head = git.get_head()
-
-    out.start('Committing %d patches' % len(applied))
-
-    crt_series.pop_patch(applied[0])
-    git.switch(crt_head)
-
-    for patch in applied:
-        crt_series.delete_patch(patch)
-
+    stack = directory.repository.current_stack
+    patches = stack.patchorder.applied
+    if not patches:
+        raise CmdException('No patches to commit')
+    out.start('Committing %d patches' % len(patches))
+    trans = transaction.StackTransaction(stack, 'stg commit')
+    for pn in patches:
+        trans.patches[pn] = None
+    trans.applied = []
+    trans.base = stack.head
+    trans.run()
     out.done()
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index 0ca647e..a7c4f7e 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -41,6 +41,7 @@ class StackTransaction(object):
         self.__unapplied = list(self.__stack.patchorder.unapplied)
         self.__error = None
         self.__current_tree = self.__stack.head.data.tree
+        self.__base = self.__stack.base
     stack = property(lambda self: self.__stack)
     patches = property(lambda self: self.__patches)
     def __set_applied(self, val):
@@ -49,6 +50,10 @@ class StackTransaction(object):
     def __set_unapplied(self, val):
         self.__unapplied = list(val)
     unapplied = property(lambda self: self.__unapplied, __set_unapplied)
+    def __set_base(self, val):
+        assert not self.__applied
+        self.__base = val
+    base = property(lambda self: self.__base, __set_base)
     def __checkout(self, tree, iw):
         if not self.__stack.head_top_equal():
             out.error(
@@ -76,7 +81,7 @@ class StackTransaction(object):
         if self.__applied:
             return self.__patches[self.__applied[-1]]
         else:
-            return self.__stack.base
+            return self.__base
     def abort(self, iw = None):
         # The only state we need to restore is index+worktree.
         if iw:
Karl Hasselström· Dec 14, 2007, 06:32 UTC · re: Karl Hasselström · lore

[StGit PATCH 5/5] Make "stg commit" fancier

Allow the user to commit any patch. Changed behavior: with no parameters, commit one applied patch, not all applied patches -- this is what uncommit does.

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/commands/commit.py   |   87 +++++++++++++++++++++++++++++++++-----------
 stgit/commands/uncommit.py |    2 +
 stgit/lib/transaction.py   |    3 +-
 t/t1300-uncommit.sh        |   12 +++---
 4 files changed, 74 insertions(+), 30 deletions(-)
Show changes to 4 files +74 −30

stgit/commands/commit.py, stgit/commands/uncommit.py, stgit/lib/transaction.py, t/t1300-uncommit.sh

diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py
index f822181..1d741b3 100644
--- a/stgit/commands/commit.py
+++ b/stgit/commands/commit.py
@@ -15,39 +15,82 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
+from optparse import make_option
 from stgit.commands import common
 from stgit.lib import transaction
 from stgit.out import *
 
 help = 'permanently store the applied patches into stack base'
-usage = """%prog [options]
+usage = """%prog [<patchnames>] | -n NUM | --all
 
-Merge the applied patches into the base of the current stack and
-remove them from the series while advancing the base.
+Merge one or more patches into the base of the current stack and
+remove them from the series while advancing the base. This is the
+opposite of 'stg uncommit'. Use this command if you no longer want to
+manage a patch with StGIT.
 
-Use this command only if you want to permanently store the applied
-patches and no longer manage them with StGIT."""
+By default, the bottommost patch is committed. If patch names are
+given, the stack is rearranged so that those patches are at the
+bottom, and then they are committed.
 
-directory = common.DirectoryHasRepositoryLib()
-options = []
+The -n/--number option specifies the number of applied patches to
+commit (counting from the bottom of the stack). If -a/--all is given,
+all applied patches are committed."""
 
+directory = common.DirectoryHasRepositoryLib()
+options = [make_option('-n', '--number', type = 'int',
+                       help = 'commit the specified number of patches'),
+           make_option('-a', '--all', action = 'store_true',
+                       help = 'commit all applied patches')]
 
 def func(parser, options, args):
-    """Merge the applied patches into the base of the current stack
-       and remove them from the series while advancing the base
-    """
-    if len(args) != 0:
-        parser.error('incorrect number of arguments')
-
+    """Commit a number of patches."""
     stack = directory.repository.current_stack
-    patches = stack.patchorder.applied
+    args = common.parse_patches(args, (list(stack.patchorder.applied)
+                                       + list(stack.patchorder.unapplied)))
+    if len([x for x in [args, options.number != None, options.all] if x]) > 1:
+        parser.error('too many options')
+    if args:
+        patches = [pn for pn in (stack.patchorder.applied
+                                 + stack.patchorder.unapplied) if pn in args]
+        bad = set(args) - set(patches)
+        if bad:
+            raise common.CmdException('Bad patch names: %s'
+                                      % ', '.join(sorted(bad)))
+    elif options.number != None:
+        if options.number <= len(stack.patchorder.applied):
+            patches = stack.patchorder.applied[:options.number]
+        else:
+            raise common.CmdException('There are not that many applied patches')
+    elif options.all:
+        patches = stack.patchorder.applied
+    else:
+        patches = stack.patchorder.applied[:1]
     if not patches:
-        raise CmdException('No patches to commit')
-    out.start('Committing %d patches' % len(patches))
+        raise common.CmdException('No patches to commit')
+
+    iw = stack.repository.default_iw()
     trans = transaction.StackTransaction(stack, 'stg commit')
-    for pn in patches:
-        trans.patches[pn] = None
-    trans.applied = []
-    trans.base = stack.head
-    trans.run()
-    out.done()
+    try:
+        common_prefix = 0
+        for i in xrange(min(len(stack.patchorder.applied), len(patches))):
+            if stack.patchorder.applied[i] == patches[i]:
+                common_prefix += 1
+        if common_prefix < len(patches):
+            to_push = trans.pop_patches(
+                lambda pn: pn in stack.patchorder.applied[common_prefix:])
+            for pn in patches[common_prefix:]:
+                trans.push_patch(pn, iw)
+        else:
+            to_push = []
+        new_base = trans.patches[patches[-1]]
+        for pn in patches:
+            trans.patches[pn] = None
+        trans.applied = [pn for pn in trans.applied if pn not in patches]
+        trans.base = new_base
+        out.info('Committed %d patch%s' % (len(patches),
+                                           ['es', ''][len(patches) == 1]))
+        for pn in to_push:
+            trans.push_patch(pn, iw)
+    except transaction.TransactionHalted:
+        pass
+    return trans.run(iw)
diff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py
index 8422952..933ec60 100644
--- a/stgit/commands/uncommit.py
+++ b/stgit/commands/uncommit.py
@@ -28,7 +28,7 @@ usage = """%prog [<patchnames>] | -n NUM [<prefix>]] | -t <committish> [-x]
 
 Take one or more git commits at the base of the current stack and turn
 them into StGIT patches. The new patches are created as applied patches
-at the bottom of the stack. This is the exact opposite of 'stg commit'.
+at the bottom of the stack. This is the opposite of 'stg commit'.
 
 By default, the number of patches to uncommit is determined by the
 number of patch names provided on the command line. First name is used
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index a7c4f7e..a60c5ff 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -51,7 +51,8 @@ class StackTransaction(object):
         self.__unapplied = list(val)
     unapplied = property(lambda self: self.__unapplied, __set_unapplied)
     def __set_base(self, val):
-        assert not self.__applied
+        assert (not self.__applied
+                or self.patches[self.applied[0]].data.parent == val)
         self.__base = val
     base = property(lambda self: self.__base, __set_base)
     def __checkout(self, tree, iw):
diff --git a/t/t1300-uncommit.sh b/t/t1300-uncommit.sh
index 85408fd..d86e579 100755
--- a/t/t1300-uncommit.sh
+++ b/t/t1300-uncommit.sh
@@ -35,7 +35,7 @@ test_expect_success \
 test_expect_success \
 	'Commit the patches' \
 	'
-	stg commit
+	stg commit --all
 	'
 
 test_expect_success \
@@ -43,7 +43,7 @@ test_expect_success \
 	'
 	stg uncommit bar foo &&
 	[ "$(stg id foo//top)" = "$(stg id bar//bottom)" ] &&
-	stg commit
+	stg commit --all
 	'
 
 test_expect_success \
@@ -51,7 +51,7 @@ test_expect_success \
 	'
 	stg uncommit --number=2 foobar &&
 	[ "$(stg id foobar1//top)" = "$(stg id foobar2//bottom)" ] &&
-	stg commit
+	stg commit --all
 	'
 
 test_expect_success \
@@ -59,7 +59,7 @@ test_expect_success \
 	'
 	stg uncommit --number=2 &&
 	[ "$(stg id foo-patch//top)" = "$(stg id bar-patch//bottom)" ] &&
-	stg commit
+	stg commit --all
 	'
 
 test_expect_success \
@@ -68,14 +68,14 @@ test_expect_success \
 	stg uncommit &&
 	stg uncommit &&
 	[ "$(stg id foo-patch//top)" = "$(stg id bar-patch//bottom)" ] &&
-	stg commit
+	stg commit --all
 	'
 
 test_expect_success \
     'Uncommit the patches with --to' '
     stg uncommit --to HEAD^ &&
     [ "$(stg id foo-patch//top)" = "$(stg id bar-patch//bottom)" ] &&
-    stg commit
+    stg commit --all
 '
 
 test_done

← back to recent threads