[StGIT PATCH 0/9] Refactoring of command handling

DORMANTno replies

10 messages, 1 author, 2016-06-15 · open the first message on its own page

[StGIT PATCH 0/9] Refactoring of command handling

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

While working on hydra support, I've hit a couple of issues which I
have handled with a set of relatively large patches: nearly every line
of code under stgit/commands/ gets reindented, because of functions
being changed to instance methods.

I submit this patchset early, despite it being very young, because of
the high risk of conflict with other work and the large amount work it
would require to maintain in parallel - also, since the testsuite does
not cover everything, it is likely that I left a couple of errors
somewhere, and I'd be grateful to have other people shake the code as
well.  Now this set of patches would be a great candidate for a "pu"
branch :)

The next refactoring pass (already in the works but with no
stabilized design yet) will tackle git_id(), which should be
available at generic level, not at the command level.  I'm heading
towards changing it to be a method of a new stgit.Repository class,
pointed to by Commands and PatchSets.

Here is a quick summary:

      Refactor command definition with a Command class.
      Promote more common functions to Command methods.
      Replace crt_series uses with a method call.
	=> those are the heart of the refactoring: we suppress the
	   crt_series global and only create the Series object when
	   needed.  This is a step towards a PatchSet factory which
	   will be able to instantiate a Series or an Hydra from a
	   head name, without a need for the called to be aware of the
	   existence of PatchSet subclasses.

      Fixed thinko in error message.
      Add a constructor to PatchSet.
      Cleanup the use of the Series class.
      Changed sync not to use -b which has other semantics.
	=> cleanup needs discovered while refactoring

      Fix contrib/stg-whatchanged way of identifying a conflict.
	=> fixing the tools that help getting the patches done :)
      Revert part of the reverted commit that we want to keep.
	=> mostly here so we don't loose it, and limit the conflicts
	  when I pulled from you :)

Best regards,
-- 
Yann Dirson    [off-list ref] |
Debian-related: [off-list ref] |   Support Debian GNU/Linux:
                                    |  Freedom, Power, Stability, Gratis
     http://ydirson.free.fr/        | Check <http://www.debian.org/>

[StGIT PATCH 1/9] Fix contrib/stg-whatchanged way of identifying a conflict.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

Signed-off-by: Yann Dirson <redacted>
---

 contrib/stg-whatchanged |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/contrib/stg-whatchanged b/contrib/stg-whatchanged
index d5b6965..27513d9 100755
--- a/contrib/stg-whatchanged
+++ b/contrib/stg-whatchanged
@@ -24,7 +24,7 @@ fi
 # Merges via "push" leave top=bottom so we must look at old patch
 # in this case (unlike, eg., "pick --fold")
 patchdir="$(git-rev-parse --git-dir)/patches/$(stg branch)/patches/$(stg top)"
-if [ $(cat "$patchdir/bottom") = $(cat "$patchdir/top") ];
+if [ -e "$(git-rev-parse --git-dir)/conflicts" ];
 then
     current_cmd="stg show //top.old"
 else

[StGIT PATCH 2/9] Revert part of the reverted commit that we want to keep.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

- get rid of unused real_rebase parameter
- revive git.all_refs()

This commit can freely be used to demonstrate the usefulness of
_always_ separating different issues in different patches :)

Signed-off-by: Yann Dirson <redacted>
---

 stgit/commands/common.py |    2 +-
 stgit/commands/pull.py   |    2 +-
 stgit/commands/rebase.py |    2 +-
 stgit/git.py             |    6 ++++++
 4 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index 22c78ae..b05979b 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -318,7 +318,7 @@ def address_or_alias(addr_str):
                  for addr in addr_str.split(',')]
     return ', '.join([addr for addr in addr_list if addr])
 
-def prepare_rebase(real_rebase, force=None):
+def prepare_rebase(force=None):
     if not force:
         # Be sure we won't loose results of stg-(un)commit by error.
         # Do not require an existing orig-base for compatibility with 0.12 and earlier.
diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index 5f72f9b..fe3b67d 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -86,7 +86,7 @@ def func(parser, options, args):
     else:
         raise GitConfigException, 'Unsupported pull-policy "%s"' % policy
 
-    applied = prepare_rebase(real_rebase=must_rebase, force=options.force)
+    applied = prepare_rebase(force=options.force)
 
     # pull the remote changes
     if policy == 'pull':
diff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py
index 2f0e660..e47772c 100644
--- a/stgit/commands/rebase.py
+++ b/stgit/commands/rebase.py
@@ -56,7 +56,7 @@ def func(parser, options, args):
     if git_id(args[0]) == None:
         raise GitException, 'Unknown revision: %s' % git_id
         
-    applied = prepare_rebase(real_rebase=True, force=options.force)
+    applied = prepare_rebase(force=options.force)
     rebase(args[0])
     post_rebase(applied, options.nopush, options.merged)
 
diff --git a/stgit/git.py b/stgit/git.py
index 6c96a16..047c040 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -1070,3 +1070,9 @@ def fetch_head():
 
     # here we are sure to have a single fetch_head
     return fetch_head
+
+def all_refs():
+    """Return a list of all refs in the current repository.
+    """
+
+    return [line.split()[1] for line in _output_lines(['git-show-ref'])]

[StGIT PATCH 4/9] Fixed thinko in error message.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

Signed-off-by: Yann Dirson <redacted>
---

 stgit/commands/rebase.py |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py
index 1cde2ad..111d78e 100644
--- a/stgit/commands/rebase.py
+++ b/stgit/commands/rebase.py
@@ -55,7 +55,7 @@ class concreteCommand(Command):
 
         # ensure an exception is raised before popping on non-existent target
         if git_id(self.args[0]) == None:
-            raise GitException, 'Unknown revision: %s' % git_id
+            raise GitException, 'Unknown revision: %s' % git_id(self.args[0])
 
         applied = prepare_rebase(force=self.flags.force)
         rebase(self.args[0])

[StGIT PATCH 6/9] Changed sync not to use -b which has other semantics.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

Using --branch here is confusing from a UI point of view, and
unnecessarily complicates implementation, making in more delicate to
get rid of the crt-series global.  We now use --ref-branch (-B)
instead.

Signed-off-by: Yann Dirson <redacted>
---

 stgit/commands/sync.py |   15 ++++++---------
 t/t2000-sync.sh        |    8 ++++----
 2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/stgit/commands/sync.py b/stgit/commands/sync.py
index 9c95b99..2882391 100644
--- a/stgit/commands/sync.py
+++ b/stgit/commands/sync.py
@@ -51,7 +51,7 @@ class concreteCommand(Command):
     options = [make_option('-a', '--all',
                            help = 'synchronise all the patches',
                            action = 'store_true'),
-               make_option('-b', '--branch',
+               make_option('-B', '--ref-branch',
                            help = 'syncronise patches with BRANCH'),
                make_option('-s', '--series',
                            help = 'syncronise patches with SERIES'),
@@ -70,9 +70,9 @@ class concreteCommand(Command):
         global crt_series
 
         if self.flags.undo:
-            if self.flags.branch or self.flags.series:
+            if self.flags.ref_branch or self.flags.series:
                 raise CmdException, \
-                      '--undo cannot be specified with --branch or --series'
+                      '--undo cannot be specified with --ref-branch or --series'
             self.__check_all()
 
             out.start('Undoing the sync of "%s"' % crt_series.get_current())
@@ -81,12 +81,9 @@ class concreteCommand(Command):
             out.done()
             return
 
-        if self.flags.branch:
-            # the main function already made crt_series to be the remote
-            # branch
-            remote_series = crt_series
-            stgit.commands.common.crt_series = crt_series = stack.Series()
-            if self.flags.branch == crt_series.get_name():
+        if self.flags.ref_branch:
+            remote_series = stack.Series(self.flags.ref_branch)
+            if self.flags.ref_branch == crt_series.get_name():
                 raise CmdException, 'Cannot synchronise with the current branch'
             remote_patches = remote_series.get_applied()
 
diff --git a/t/t2000-sync.sh b/t/t2000-sync.sh
index 69ab1ac..f831df7 100755
--- a/t/t2000-sync.sh
+++ b/t/t2000-sync.sh
@@ -48,7 +48,7 @@ test_expect_success \
 test_expect_success \
     'Synchronise second patch with the master branch' \
     '
-    stg sync -b master p2 &&
+    stg sync -B master p2 &&
     [ "$(echo $(stg applied))" = "p1 p2 p3" ] &&
     [ "$(echo $(stg unapplied))" = "" ] &&
     test $(cat foo2.txt) = "foo2"
@@ -57,7 +57,7 @@ test_expect_success \
 test_expect_success \
     'Synchronise the first two patches with the master branch' \
     '
-    stg sync -b master -a &&
+    stg sync -B master -a &&
     [ "$(echo $(stg applied))" = "p1 p2 p3" ] &&
     [ "$(echo $(stg unapplied))" = "" ] &&
     test $(cat foo1.txt) = "foo1" &&
@@ -100,7 +100,7 @@ test_expect_success \
 test_expect_success \
     'Synchronise second patch with the master branch' \
     '
-    stg sync -b master p2 &&
+    stg sync -B master p2 &&
     [ "$(echo $(stg applied))" = "p1 p2 p3" ] &&
     [ "$(echo $(stg unapplied))" = "" ] &&
     test $(cat bar2.txt) = "bar2"
@@ -109,7 +109,7 @@ test_expect_success \
 test_expect_failure \
     'Synchronise the first two patches with the master branch (to fail)' \
     '
-    stg sync -b master -a
+    stg sync -B master -a
     '
 
 test_expect_success \

[StGIT PATCH 5/9] Promote more common functions to Command methods.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

Signed-off-by: Yann Dirson <redacted>
---

 stgit/commands/branch.py  |   10 +-
 stgit/commands/clean.py   |    4 -
 stgit/commands/commit.py  |    2 
 stgit/commands/common.py  |  270 +++++++++++++++++++++++----------------------
 stgit/commands/delete.py  |    4 -
 stgit/commands/diff.py    |    4 -
 stgit/commands/files.py   |    4 -
 stgit/commands/float.py   |    6 +
 stgit/commands/fold.py    |    4 -
 stgit/commands/goto.py    |    8 +
 stgit/commands/id.py      |    2 
 stgit/commands/imprt.py   |    6 +
 stgit/commands/mail.py    |    8 +
 stgit/commands/new.py     |    2 
 stgit/commands/pick.py    |   12 +-
 stgit/commands/pop.py     |    8 +
 stgit/commands/pull.py    |   12 +-
 stgit/commands/push.py    |    8 +
 stgit/commands/rebase.py  |   14 +-
 stgit/commands/refresh.py |    6 +
 stgit/commands/series.py  |    4 -
 stgit/commands/show.py    |    2 
 stgit/commands/sink.py    |    6 +
 stgit/commands/sync.py    |    8 +
 24 files changed, 207 insertions(+), 207 deletions(-)
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index 9777fad..e243830 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -98,7 +98,7 @@ class concreteCommand(Command):
         if self.__is_current_branch(doomed_name):
             check_local_changes()
             check_conflicts()
-            check_head_top_equal()
+            self.check_head_top_equal()
 
             if doomed_name != 'master':
                 git.switch_branch('master')
@@ -119,7 +119,7 @@ class concreteCommand(Command):
 
             check_local_changes()
             check_conflicts()
-            check_head_top_equal()
+            self.check_head_top_equal()
 
             tree_id = None
             if len(self.args) >= 2:
@@ -143,7 +143,7 @@ class concreteCommand(Command):
                              ' from "%s"' % self.args[1])
                     parentbranch = None
 
-                tree_id = git_id(self.args[1])
+                tree_id = self.git_id(self.args[1])
             else:
                 # branch stack off current branch
                 parentbranch = git.get_head_file()
@@ -178,7 +178,7 @@ class concreteCommand(Command):
 
             check_local_changes()
             check_conflicts()
-            check_head_top_equal()
+            self.check_head_top_equal()
 
             out.start('Cloning current branch to "%s"' % clone)
             crt_series.clone(clone)
@@ -293,7 +293,7 @@ class concreteCommand(Command):
 
             check_local_changes()
             check_conflicts()
-            check_head_top_equal()
+            self.check_head_top_equal()
 
             out.start('Switching to branch "%s"' % self.args[0])
             git.switch_branch(self.args[0])
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index 10ee89e..3e9d829 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -60,7 +60,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         if not (self.flags.applied or self.flags.unapplied):
             self.flags.applied = self.flags.unapplied = True
@@ -73,4 +73,4 @@ class concreteCommand(Command):
             unapplied = crt_series.get_unapplied()
             self.__delete_empty(unapplied, False)
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py
index 6e7bf13..56f3943 100644
--- a/stgit/commands/commit.py
+++ b/stgit/commands/commit.py
@@ -42,7 +42,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         applied = crt_series.get_applied()
         if not applied:
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index 60a6a7a..1ed5086 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -71,6 +71,141 @@ class Command:
         self.parser = OptionParser(usage = shortusage, option_list = self.options)
         self.flags, self.args = self.parser.parse_args()
 
+    def git_id(self, rev):
+        """Return the GIT id
+        """
+        if not rev:
+            return None
+        try:
+            patch, branch, patch_id = parse_rev(rev)
+            if branch == None:
+                series = crt_series
+            else:
+                series = stack.Series(branch)
+            if patch == None:
+                patch = series.get_current()
+                if not patch:
+                    raise CmdException, 'No patches applied'
+            if patch in series.get_applied() or patch in series.get_unapplied():
+                if patch_id in ['top', '', None]:
+                    return series.get_patch(patch).get_top()
+                elif patch_id == 'bottom':
+                    return series.get_patch(patch).get_bottom()
+                elif patch_id == 'top.old':
+                    return series.get_patch(patch).get_old_top()
+                elif patch_id == 'bottom.old':
+                    return series.get_patch(patch).get_old_bottom()
+                elif patch_id == 'log':
+                    return series.get_patch(patch).get_log()
+            if patch == 'base' and patch_id == None:
+                return series.get_base()
+        except RevParseException:
+            pass
+        return git.rev_parse(rev + '^{commit}')
+
+    def check_head_top_equal(self):
+        if not crt_series.head_top_equal():
+            raise CmdException(
+                'HEAD and top are not the same. You probably committed\n'
+                '  changes to the tree outside of StGIT. To bring them\n'
+                '  into StGIT, use the "assimilate" command')
+
+    def print_crt_patch(self, branch = None):
+        if not branch:
+            patch = crt_series.get_current()
+        else:
+            patch = stack.Series(branch).get_current()
+
+        if patch:
+            out.info('Now at patch "%s"' % patch)
+        else:
+            out.info('No patches applied')
+
+    def push_patches(self, patches, check_merged = False):
+        """Push multiple patches onto the stack. This function is shared
+        between the push and pull commands
+        """
+        forwarded = crt_series.forward_patches(patches)
+        if forwarded > 1:
+            out.info('Fast-forwarded patches "%s" - "%s"'
+                     % (patches[0], patches[forwarded - 1]))
+        elif forwarded == 1:
+            out.info('Fast-forwarded patch "%s"' % patches[0])
+
+        names = patches[forwarded:]
+
+        # check for patches merged upstream
+        if names and check_merged:
+            out.start('Checking for patches merged upstream')
+
+            merged = crt_series.merged_patches(names)
+
+            out.done('%d found' % len(merged))
+        else:
+            merged = []
+
+        for p in names:
+            out.start('Pushing patch "%s"' % p)
+
+            if p in merged:
+                crt_series.push_patch(p, empty = True)
+                out.done('merged upstream')
+            else:
+                modified = crt_series.push_patch(p)
+
+                if crt_series.empty_patch(p):
+                    out.done('empty patch')
+                elif modified:
+                    out.done('modified')
+                else:
+                    out.done()
+
+    def pop_patches(self, patches, keep = False):
+        """Pop the patches in the list from the stack. It is assumed that
+        the patches are listed in the stack reverse order.
+        """
+        if len(patches) == 0:
+            out.info('Nothing to push/pop')
+        else:
+            p = patches[-1]
+            if len(patches) == 1:
+                out.start('Popping patch "%s"' % p)
+            else:
+                out.start('Popping patches "%s" - "%s"' % (patches[0], p))
+            crt_series.pop_patch(p, keep)
+            out.done()
+
+    def prepare_rebase(self, force=None):
+        if not force:
+            # Be sure we won't loose results of stg-(un)commit by error.
+            # Do not require an existing orig-base for compatibility with 0.12 and earlier.
+            origbase = crt_series._get_field('orig-base')
+            if origbase and crt_series.get_base() != origbase:
+                raise CmdException, 'Rebasing would possibly lose data'
+
+        # pop all patches
+        applied = crt_series.get_applied()
+        if len(applied) > 0:
+            out.start('Popping all applied patches')
+            crt_series.pop_patch(applied[0])
+            out.done()
+        return applied
+
+    def rebase(self, target):
+        if target == git.get_head():
+            out.info('Already at "%s", no need for rebasing.' % target)
+            return
+        out.start('Rebasing to "%s"' % target)
+        git.reset(tree_id = self.git_id(target))
+        out.done()
+
+    def post_rebase(self, applied, nopush, merged):
+        # memorize that we rebased to here
+        crt_series._set_field('orig-base', git.get_head())
+        # push the patches back
+        if not nopush:
+            self.push_patches(applied, merged)
+
 # Utility functions
 def parse_rev(rev):
     """Parse a revision specification into its
@@ -108,67 +243,17 @@ def parse_rev(rev):
     # No, we can't parse that.
     raise RevParseException
 
-def git_id(rev):
-    """Return the GIT id
-    """
-    if not rev:
-        return None
-    try:
-        patch, branch, patch_id = parse_rev(rev)
-        if branch == None:
-            series = crt_series
-        else:
-            series = stack.Series(branch)
-        if patch == None:
-            patch = series.get_current()
-            if not patch:
-                raise CmdException, 'No patches applied'
-        if patch in series.get_applied() or patch in series.get_unapplied():
-            if patch_id in ['top', '', None]:
-                return series.get_patch(patch).get_top()
-            elif patch_id == 'bottom':
-                return series.get_patch(patch).get_bottom()
-            elif patch_id == 'top.old':
-                return series.get_patch(patch).get_old_top()
-            elif patch_id == 'bottom.old':
-                return series.get_patch(patch).get_old_bottom()
-            elif patch_id == 'log':
-                return series.get_patch(patch).get_log()
-        if patch == 'base' and patch_id == None:
-            return series.get_base()
-    except RevParseException:
-        pass
-    return git.rev_parse(rev + '^{commit}')
-
 def check_local_changes():
     if git.local_changes():
         raise CmdException, \
               'local changes in the tree. Use "refresh" or "status --reset"'
 
-def check_head_top_equal():
-    if not crt_series.head_top_equal():
-        raise CmdException(
-            'HEAD and top are not the same. You probably committed\n'
-            '  changes to the tree outside of StGIT. To bring them\n'
-            '  into StGIT, use the "assimilate" command')
-
 def check_conflicts():
     if os.path.exists(os.path.join(basedir.get(), 'conflicts')):
         raise CmdException, \
               'Unsolved conflicts. Please resolve them first or\n' \
               '  revert the changes with "status --reset"'
 
-def print_crt_patch(branch = None):
-    if not branch:
-        patch = crt_series.get_current()
-    else:
-        patch = stack.Series(branch).get_current()
-
-    if patch:
-        out.info('Now at patch "%s"' % patch)
-    else:
-        out.info('No patches applied')
-
 def resolved(filename, reset = None):
     if reset:
         reset_file = filename + file_extensions()[reset]
@@ -191,60 +276,6 @@ def resolved_all(reset = None):
             resolved(filename, reset)
         os.remove(os.path.join(basedir.get(), 'conflicts'))
 
-def push_patches(patches, check_merged = False):
-    """Push multiple patches onto the stack. This function is shared
-    between the push and pull commands
-    """
-    forwarded = crt_series.forward_patches(patches)
-    if forwarded > 1:
-        out.info('Fast-forwarded patches "%s" - "%s"'
-                 % (patches[0], patches[forwarded - 1]))
-    elif forwarded == 1:
-        out.info('Fast-forwarded patch "%s"' % patches[0])
-
-    names = patches[forwarded:]
-
-    # check for patches merged upstream
-    if names and check_merged:
-        out.start('Checking for patches merged upstream')
-
-        merged = crt_series.merged_patches(names)
-
-        out.done('%d found' % len(merged))
-    else:
-        merged = []
-
-    for p in names:
-        out.start('Pushing patch "%s"' % p)
-
-        if p in merged:
-            crt_series.push_patch(p, empty = True)
-            out.done('merged upstream')
-        else:
-            modified = crt_series.push_patch(p)
-
-            if crt_series.empty_patch(p):
-                out.done('empty patch')
-            elif modified:
-                out.done('modified')
-            else:
-                out.done()
-
-def pop_patches(patches, keep = False):
-    """Pop the patches in the list from the stack. It is assumed that
-    the patches are listed in the stack reverse order.
-    """
-    if len(patches) == 0:
-        out.info('Nothing to push/pop')
-    else:
-        p = patches[-1]
-        if len(patches) == 1:
-            out.start('Popping patch "%s"' % p)
-        else:
-            out.start('Popping patches "%s" - "%s"' % (patches[0], p))
-        crt_series.pop_patch(p, keep)
-        out.done()
-
 def parse_patches(patch_args, patch_list, boundary = 0, ordered = False):
     """Parse patch_args list for patch names in patch_list and return
     a list. The names can be individual patches and/or in the
@@ -351,34 +382,3 @@ def address_or_alias(addr_str):
     addr_list = [__address_or_alias(addr.strip())
                  for addr in addr_str.split(',')]
     return ', '.join([addr for addr in addr_list if addr])
-
-def prepare_rebase(force=None):
-    if not force:
-        # Be sure we won't loose results of stg-(un)commit by error.
-        # Do not require an existing orig-base for compatibility with 0.12 and earlier.
-        origbase = crt_series._get_field('orig-base')
-        if origbase and crt_series.get_base() != origbase:
-            raise CmdException, 'Rebasing would possibly lose data'
-
-    # pop all patches
-    applied = crt_series.get_applied()
-    if len(applied) > 0:
-        out.start('Popping all applied patches')
-        crt_series.pop_patch(applied[0])
-        out.done()
-    return applied
-
-def rebase(target):
-    if target == git.get_head():
-        out.info('Already at "%s", no need for rebasing.' % target)
-        return
-    out.start('Rebasing to "%s"' % target)
-    git.reset(tree_id = git_id(target))
-    out.done()
-
-def post_rebase(applied, nopush, merged):
-    # memorize that we rebased to here
-    crt_series._set_field('orig-base', git.get_head())
-    # push the patches back
-    if not nopush:
-        push_patches(applied, merged)
diff --git a/stgit/commands/delete.py b/stgit/commands/delete.py
index 5a958ca..6d10895 100644
--- a/stgit/commands/delete.py
+++ b/stgit/commands/delete.py
@@ -70,7 +70,7 @@ class concreteCommand(Command):
         if applied and not self.flags.branch:
             check_local_changes()
             check_conflicts()
-            check_head_top_equal()
+            self.check_head_top_equal()
 
         # delete the patches
         for patch in applied + patches:
@@ -78,4 +78,4 @@ class concreteCommand(Command):
             out.info('Patch "%s" successfully deleted' % patch)
 
         if not self.flags.branch:
-            print_crt_patch()
+            self.print_crt_patch()
diff --git a/stgit/commands/diff.py b/stgit/commands/diff.py
index a015be7..1344953 100644
--- a/stgit/commands/diff.py
+++ b/stgit/commands/diff.py
@@ -85,9 +85,9 @@ class concreteCommand(Command):
             diff_flags = []
 
         if self.flags.stat:
-            out.stdout_raw(git.diffstat(self.args, git_id(rev1), git_id(rev2)) + '\n')
+            out.stdout_raw(git.diffstat(self.args, self.git_id(rev1), self.git_id(rev2)) + '\n')
         else:
-            diff_str = git.diff(self.args, git_id(rev1), git_id(rev2),
+            diff_str = git.diff(self.args, self.git_id(rev1), self.git_id(rev2),
                                 diff_flags = diff_flags )
             if diff_str:
                 pager(diff_str)
diff --git a/stgit/commands/files.py b/stgit/commands/files.py
index dbdbf11..ad3a5d1 100644
--- a/stgit/commands/files.py
+++ b/stgit/commands/files.py
@@ -56,8 +56,8 @@ class concreteCommand(Command):
         else:
             self.parser.error('incorrect number of arguments')
 
-        rev1 = git_id('%s//bottom' % patch)
-        rev2 = git_id('%s//top' % patch)
+        rev1 = self.git_id('%s//bottom' % patch)
+        rev2 = self.git_id('%s//top' % patch)
 
         if self.flags.stat:
             out.stdout_raw(git.diffstat(rev1 = rev1, rev2 = rev2) + '\n')
diff --git a/stgit/commands/float.py b/stgit/commands/float.py
index 7acd70c..ea82ea5 100644
--- a/stgit/commands/float.py
+++ b/stgit/commands/float.py
@@ -46,7 +46,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         unapplied = crt_series.get_unapplied()
         applied = crt_series.get_applied()
@@ -85,7 +85,7 @@ class concreteCommand(Command):
         # check whether the operation is really needed
         if topop != topush:
             if topop:
-                pop_patches(topop)
+                self.pop_patches(topop)
             if topush:
                 topush.reverse()
-                push_patches(topush)
+                self.push_patches(topush)
diff --git a/stgit/commands/fold.py b/stgit/commands/fold.py
index 73612fc..6d75df5 100644
--- a/stgit/commands/fold.py
+++ b/stgit/commands/fold.py
@@ -49,7 +49,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         if len(self.args) == 1:
             filename = self.args[0]
@@ -73,7 +73,7 @@ class concreteCommand(Command):
             bottom = crt_patch.get_bottom()
             git.apply_patch(filename = filename, base = bottom)
         elif self.flags.base:
-            git.apply_patch(filename = filename, base = git_id(self.flags.base))
+            git.apply_patch(filename = filename, base = self.git_id(self.flags.base))
         else:
             git.apply_patch(filename = filename)
 
diff --git a/stgit/commands/goto.py b/stgit/commands/goto.py
index 0ca6bc3..ceec9ea 100644
--- a/stgit/commands/goto.py
+++ b/stgit/commands/goto.py
@@ -43,7 +43,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         applied = crt_series.get_applied()
         unapplied = crt_series.get_unapplied()
@@ -52,11 +52,11 @@ class concreteCommand(Command):
         if patch in applied:
             applied.reverse()
             patches = applied[:applied.index(patch)]
-            pop_patches(patches)
+            self.pop_patches(patches)
         elif patch in unapplied:
             patches = unapplied[:unapplied.index(patch)+1]
-            push_patches(patches)
+            self.push_patches(patches)
         else:
             raise CmdException, 'Patch "%s" does not exist' % patch
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/id.py b/stgit/commands/id.py
index 50ddc42..64f5132 100644
--- a/stgit/commands/id.py
+++ b/stgit/commands/id.py
@@ -47,4 +47,4 @@ class concreteCommand(Command):
         else:
             self.parser.error('incorrect number of arguments')
 
-        out.stdout(git_id(id_str))
+        out.stdout(self.git_id(id_str))
diff --git a/stgit/commands/imprt.py b/stgit/commands/imprt.py
index 5c2a95f..2401c4a 100644
--- a/stgit/commands/imprt.py
+++ b/stgit/commands/imprt.py
@@ -292,7 +292,7 @@ class concreteCommand(Command):
 
         out.start('Importing patch "%s"' % patch)
         if self.flags.base:
-            git.apply_patch(diff = diff, base = git_id(self.flags.base))
+            git.apply_patch(diff = diff, base = self.git_id(self.flags.base))
         else:
             git.apply_patch(diff = diff)
         crt_series.refresh_patch(edit = self.flags.edit,
@@ -396,7 +396,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         if len(self.args) == 1:
             filename = self.args[0]
@@ -412,4 +412,4 @@ class concreteCommand(Command):
         else:
             self.__import_file(filename)
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index b8bf1d2..cdc0e2a 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -405,11 +405,11 @@ class concreteCommand(Command):
                      'longdescr':    long_descr,
                      # for backward template compatibility
                      'endofheaders': '',
-                     'diff':         git.diff(rev1 = git_id('%s//bottom' % patch),
-                                              rev2 = git_id('%s//top' % patch),
+                     'diff':         git.diff(rev1 = self.git_id('%s//bottom' % patch),
+                                              rev2 = self.git_id('%s//top' % patch),
                                               diff_flags = diff_flags ),
-                     'diffstat':     git.diffstat(rev1 = git_id('%s//bottom'%patch),
-                                                  rev2 = git_id('%s//top' % patch)),
+                     'diffstat':     git.diffstat(rev1 = self.git_id('%s//bottom'%patch),
+                                                  rev2 = self.git_id('%s//top' % patch)),
                      # for backward template compatibility
                      'date':         '',
                      'version':      version_str,
diff --git a/stgit/commands/new.py b/stgit/commands/new.py
index 2b7bd3d..87f3874 100644
--- a/stgit/commands/new.py
+++ b/stgit/commands/new.py
@@ -69,7 +69,7 @@ class concreteCommand(Command):
             self.parser.error('incorrect number of arguments')
 
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         if self.flags.author:
             self.flags.authname, self.flags.authemail = name_email(self.flags.author)
diff --git a/stgit/commands/pick.py b/stgit/commands/pick.py
index 1ff9d05..6ec8be3 100644
--- a/stgit/commands/pick.py
+++ b/stgit/commands/pick.py
@@ -61,10 +61,10 @@ class concreteCommand(Command):
         if not self.flags.unapplied:
             check_local_changes()
             check_conflicts()
-            check_head_top_equal()
+            self.check_head_top_equal()
 
         commit_str = self.args[0]
-        commit_id = git_id(commit_str)
+        commit_id = self.git_id(commit_str)
         commit = git.Commit(commit_id)
 
         if self.flags.fold or self.flags.update:
@@ -80,7 +80,7 @@ class concreteCommand(Command):
                 patchname = None
 
         if self.flags.parent:
-            parent = git_id(self.flags.parent)
+            parent = self.git_id(self.flags.parent)
         else:
             parent = commit.get_parent()
 
@@ -100,8 +100,8 @@ class concreteCommand(Command):
 
             out.done()
         elif self.flags.update:
-            rev1 = git_id('//bottom')
-            rev2 = git_id('//top')
+            rev1 = self.git_id('//bottom')
+            rev2 = self.git_id('//top')
             files = git.barefiles(rev1, rev2).split('\n')
 
             out.start('Updating with commit %s' % commit_id)
@@ -157,4 +157,4 @@ class concreteCommand(Command):
             else:
                 out.done()
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/pop.py b/stgit/commands/pop.py
index cd5c46d..3b8b536 100644
--- a/stgit/commands/pop.py
+++ b/stgit/commands/pop.py
@@ -51,7 +51,7 @@ class concreteCommand(Command):
         """Pop the topmost patch from the stack
         """
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         if not self.flags.keep:
             check_local_changes()
@@ -83,8 +83,8 @@ class concreteCommand(Command):
             raise CmdException, 'Cannot pop arbitrary patches with --keep'
 
         topop.reverse()
-        pop_patches(topop, self.flags.keep)
+        self.pop_patches(topop, self.flags.keep)
         if topush:
-            push_patches(topush)
+            self.push_patches(topush)
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index 3fdbc74..06d9aa9 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -76,7 +76,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         if policy == 'pull':
             must_rebase = 0
@@ -87,7 +87,7 @@ class concreteCommand(Command):
         else:
             raise GitConfigException, 'Unsupported pull-policy "%s"' % policy
 
-        applied = prepare_rebase(force=self.flags.force)
+        applied = self.prepare_rebase(force=self.flags.force)
 
         # pull the remote changes
         if policy == 'pull':
@@ -96,14 +96,14 @@ class concreteCommand(Command):
         elif policy == 'fetch-rebase':
             out.info('Fetching from "%s"' % repository)
             git.fetch(repository)
-            rebase(git.fetch_head())
+            self.rebase(git.fetch_head())
         elif policy == 'rebase':
-            rebase(crt_series.get_parent_branch())
+            self.rebase(crt_series.get_parent_branch())
 
-        post_rebase(applied, self.flags.nopush, self.flags.merged)
+        self.post_rebase(applied, self.flags.nopush, self.flags.merged)
 
         # maybe tidy up
         if config.get('stgit.keepoptimized') == 'yes':
             git.repack()
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/push.py b/stgit/commands/push.py
index 14e8e41..0d41cad 100644
--- a/stgit/commands/push.py
+++ b/stgit/commands/push.py
@@ -71,13 +71,13 @@ class concreteCommand(Command):
                 out.done()
             else:
                 out.done('patch unchanged')
-            print_crt_patch()
+            self.print_crt_patch()
 
             return
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         unapplied = crt_series.get_unapplied()
         if not unapplied:
@@ -98,6 +98,6 @@ class concreteCommand(Command):
         if self.flags.reverse:
             patches.reverse()
 
-        push_patches(patches, self.flags.merged)
+        self.push_patches(patches, self.flags.merged)
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py
index 111d78e..f953d6b 100644
--- a/stgit/commands/rebase.py
+++ b/stgit/commands/rebase.py
@@ -51,14 +51,14 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         # ensure an exception is raised before popping on non-existent target
-        if git_id(self.args[0]) == None:
-            raise GitException, 'Unknown revision: %s' % git_id(self.args[0])
+        if self.git_id(self.args[0]) == None:
+            raise GitException, 'Unknown revision: %s' % self.git_id(self.args[0])
 
-        applied = prepare_rebase(force=self.flags.force)
-        rebase(self.args[0])
-        post_rebase(applied, self.flags.nopush, self.flags.merged)
+        applied = self.prepare_rebase(force=self.flags.force)
+        self.rebase(self.args[0])
+        self.post_rebase(applied, self.flags.nopush, self.flags.merged)
 
-        print_crt_patch()
+        self.print_crt_patch()
diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py
index e6a6953..90eb26b 100644
--- a/stgit/commands/refresh.py
+++ b/stgit/commands/refresh.py
@@ -96,7 +96,7 @@ class concreteCommand(Command):
                 raise CmdException, 'No patches applied'
 
         if not self.flags.force:
-            check_head_top_equal()
+            self.check_head_top_equal()
 
         if self.flags.undo:
             out.start('Undoing the refresh of "%s"' % patch)
@@ -126,7 +126,7 @@ class concreteCommand(Command):
             if self.flags.patch:
                 applied = crt_series.get_applied()
                 between = applied[:applied.index(patch):-1]
-                pop_patches(between, keep = True)
+                self.pop_patches(between, keep = True)
 
             out.start('Refreshing patch "%s"' % patch)
 
@@ -150,6 +150,6 @@ class concreteCommand(Command):
 
             if self.flags.patch:
                 between.reverse()
-                push_patches(between)
+                self.push_patches(between)
         else:
             out.info('Patch "%s" is already up to date' % patch)
diff --git a/stgit/commands/series.py b/stgit/commands/series.py
index ebb1ce4..2afa013 100644
--- a/stgit/commands/series.py
+++ b/stgit/commands/series.py
@@ -191,12 +191,12 @@ class concreteCommand(Command):
                 raise CmdException, '--graphical not supported with --missing'
 
             if applied:
-                gitk_args = ' %s^..%s' % (git_id(applied[0]), git_id(applied[-1]))
+                gitk_args = ' %s^..%s' % (self.git_id(applied[0]), self.git_id(applied[-1]))
             else:
                 gitk_args = ''
 
             for p in unapplied:
-                patch_id = git_id(p)
+                patch_id = self.git_id(p)
                 gitk_args += ' %s^..%s' % (patch_id, patch_id)
 
             if os.system('gitk%s' % gitk_args) != 0:
diff --git a/stgit/commands/show.py b/stgit/commands/show.py
index c207e45..5ff772f 100644
--- a/stgit/commands/show.py
+++ b/stgit/commands/show.py
@@ -66,7 +66,7 @@ class concreteCommand(Command):
         else:
             diff_flags = []
 
-        commit_ids = [git_id(patch) for patch in patches]
+        commit_ids = [self.git_id(patch) for patch in patches]
         commit_str = '\n'.join([git.pretty_commit(commit_id, diff_flags=diff_flags)
                                 for commit_id in commit_ids])
         if commit_str:
diff --git a/stgit/commands/sink.py b/stgit/commands/sink.py
index 5ec0f1d..c3ef4d5 100644
--- a/stgit/commands/sink.py
+++ b/stgit/commands/sink.py
@@ -45,7 +45,7 @@ class concreteCommand(Command):
 
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
         oldapplied = crt_series.get_applied()
         unapplied = crt_series.get_unapplied()
@@ -57,10 +57,10 @@ class concreteCommand(Command):
             patches = [ crt_series.get_current() ]
 
         crt_series.pop_patch(self.flags.to or oldapplied[0])
-        push_patches(patches)
+        self.push_patches(patches)
 
         if not self.flags.nopush:
             newapplied = crt_series.get_applied()
             def not_reapplied_yet(p):
                 return not p in newapplied
-            push_patches(filter(not_reapplied_yet, oldapplied))
+            self.push_patches(filter(not_reapplied_yet, oldapplied))
diff --git a/stgit/commands/sync.py b/stgit/commands/sync.py
index 513e7bb..9c95b99 100644
--- a/stgit/commands/sync.py
+++ b/stgit/commands/sync.py
@@ -62,7 +62,7 @@ class concreteCommand(Command):
     def __check_all(self):
         check_local_changes()
         check_conflicts()
-        check_head_top_equal()
+        self.check_head_top_equal()
 
     def func(self):
         """Synchronise a range of patches
@@ -135,13 +135,13 @@ class concreteCommand(Command):
         # pop to the one before the first patch to be synchronised
         popped = applied[applied.index(sync_patches[0]) + 1:]
         if popped:
-            pop_patches(popped[::-1])
+            self.pop_patches(popped[::-1])
 
         for p in sync_patches:
             if p in popped:
                 # push to this patch
                 idx = popped.index(p) + 1
-                push_patches(popped[:idx])
+                self.push_patches(popped[:idx])
                 del popped[:idx]
 
             # the actual sync
@@ -170,4 +170,4 @@ class concreteCommand(Command):
 
         # push the remaining patches
         if popped:
-            push_patches(popped)
+            self.push_patches(popped)

[StGIT PATCH 9/9] Cleanup the use of the Series class.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

Signed-off-by: Yann Dirson <redacted>
---

 stgit/commands/pick.py |    3 +--
 stgit/stack.py         |    2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/stgit/commands/pick.py b/stgit/commands/pick.py
index 0fb7282..9dd49d2 100644
--- a/stgit/commands/pick.py
+++ b/stgit/commands/pick.py
@@ -21,7 +21,6 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
-from stgit.stack import Series
 
 
 class concreteCommand(Command):
@@ -133,7 +132,7 @@ class concreteCommand(Command):
                 if refbranchname:
                     # assume the refseries is OK, since we already resolved
                     # commit_str to a git_id
-                    refseries = Series(refbranchname)
+                    refseries = stack.Series(refbranchname)
                 else:
                     refseries = self.current_head()
                 patch = refseries.get_patch(refpatchname)
diff --git a/stgit/stack.py b/stgit/stack.py
index e33fe62..8acb308 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -629,8 +629,8 @@ class Series(PatchSet):
             base = self.get_base()
         except:
             base = git.get_head()
-        Series(target_series).init(create_at = base)
         new_series = Series(target_series)
+        new_series.init(create_at = base)
 
         # generate an artificial description file
         new_series.set_description('clone of "%s"' % self.get_name())

[StGIT PATCH 8/9] Add a constructor to PatchSet.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

Move __base_dir up into PatchSet as well, and add an accessor.

Signed-off-by: Yann Dirson <redacted>
---

 stgit/stack.py |   38 ++++++++++++++++++++++----------------
 1 files changed, 22 insertions(+), 16 deletions(-)
diff --git a/stgit/stack.py b/stgit/stack.py
index 634588d..e33fe62 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -277,11 +277,26 @@ class Patch(StgitObject):
 FORMAT_VERSION = 2
 
 class PatchSet(StgitObject):
+    def __init__(self, name = None):
+        try:
+            if name:
+                self.set_name (name)
+            else:
+                self.set_name (git.get_head_file())
+            self.__base_dir = basedir.get()
+        except git.GitException, ex:
+            raise StackException, 'GIT tree not initialised: %s' % ex
+
+        self._set_dir(os.path.join(self.__base_dir, 'patches', self.get_name()))
+
     def get_name(self):
         return self.__name
     def set_name(self, name):
         self.__name = name
 
+    def _basedir(self):
+        return self.__base_dir
+
     def get_head(self):
         """Return the head of the branch
         """
@@ -337,22 +352,13 @@ class Series(PatchSet):
     def __init__(self, name = None):
         """Takes a series name as the parameter.
         """
-        try:
-            if name:
-                self.set_name (name)
-            else:
-                self.set_name (git.get_head_file())
-            self.__base_dir = basedir.get()
-        except git.GitException, ex:
-            raise StackException, 'GIT tree not initialised: %s' % ex
-
-        self._set_dir(os.path.join(self.__base_dir, 'patches', self.get_name()))
+        PatchSet.__init__(self, name)
 
         # Update the branch to the latest format version if it is
         # initialized, but don't touch it if it isn't.
         self.update_to_current_format_version()
 
-        self.__refs_dir = os.path.join(self.__base_dir, 'refs', 'patches',
+        self.__refs_dir = os.path.join(self._basedir(), 'refs', 'patches',
                                        self.get_name())
 
         self.__applied_file = os.path.join(self._dir(), 'applied')
@@ -374,7 +380,7 @@ class Series(PatchSet):
         possible on external functions that may change during a format
         version bump, since it must remain able to process older formats."""
 
-        branch_dir = os.path.join(self.__base_dir, 'patches', self.get_name())
+        branch_dir = os.path.join(self._basedir(), 'patches', self.get_name())
         def get_format_version():
             """Return the integer format version number, or None if the
             branch doesn't have any StGIT metadata at all, of any version."""
@@ -416,7 +422,7 @@ class Series(PatchSet):
             mkdir(os.path.join(branch_dir, 'trash'))
             patch_dir = os.path.join(branch_dir, 'patches')
             mkdir(patch_dir)
-            refs_dir = os.path.join(self.__base_dir, 'refs', 'patches', self.get_name())
+            refs_dir = os.path.join(self._basedir(), 'refs', 'patches', self.get_name())
             mkdir(refs_dir)
             for patch in (file(os.path.join(branch_dir, 'unapplied')).readlines()
                           + file(os.path.join(branch_dir, 'applied')).readlines()):
@@ -435,7 +441,7 @@ class Series(PatchSet):
                     config.set('branch.%s.description' % self.get_name(), desc)
                 rm(desc_file)
             rm(os.path.join(branch_dir, 'current'))
-            rm(os.path.join(self.__base_dir, 'refs', 'bases', self.get_name()))
+            rm(os.path.join(self._basedir(), 'refs', 'bases', self.get_name()))
             set_format_version(2)
 
         # Make sure we're at the latest version.
@@ -603,10 +609,10 @@ class Series(PatchSet):
         git.rename_branch(self.get_name(), to_name)
 
         if os.path.isdir(self._dir()):
-            rename(os.path.join(self.__base_dir, 'patches'),
+            rename(os.path.join(self._basedir(), 'patches'),
                    self.get_name(), to_stack.get_name())
         if os.path.exists(self.__refs_dir):
-            rename(os.path.join(self.__base_dir, 'refs', 'patches'),
+            rename(os.path.join(self._basedir(), 'refs', 'patches'),
                    self.get_name(), to_stack.get_name())
 
         # Rename the config section

[StGIT PATCH 7/9] Replace crt_series uses with a method call.

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

This is a first step towards use of a factory method.

It also allows to get on-demand instantiation of the "current series"
object, and we can avoid calling a Series constructor when only the
current head's name is needed.

At the same time, cleaned up how "series --missing" was implemented.

Note: current_head() is actually slightly misnamed: it is expected to
be the Series/PatchSet object on which the command operates, but for
some commands (notably stg-branch and stg-clone) the semantics are a
bit different.  Thus there is probably some more refactoring to do
here, and I'm keeping this name as a reminder.

Signed-off-by: Yann Dirson <redacted>
---

 stgit/commands/add.py        |    2 +-
 stgit/commands/applied.py    |    2 +-
 stgit/commands/assimilate.py |    8 ++++----
 stgit/commands/branch.py     |   14 +++++++-------
 stgit/commands/clean.py      |   16 ++++++++--------
 stgit/commands/commit.py     |    8 ++++----
 stgit/commands/common.py     |   36 ++++++++++++++++++------------------
 stgit/commands/copy.py       |    2 +-
 stgit/commands/delete.py     |    6 +++---
 stgit/commands/export.py     |    8 ++++----
 stgit/commands/float.py      |    4 ++--
 stgit/commands/fold.py       |    4 ++--
 stgit/commands/goto.py       |    4 ++--
 stgit/commands/hide.py       |    6 +++---
 stgit/commands/imprt.py      |   26 +++++++++++++-------------
 stgit/commands/init.py       |    2 +-
 stgit/commands/log.py        |    6 +++---
 stgit/commands/mail.py       |    6 +++---
 stgit/commands/new.py        |   14 +++++++-------
 stgit/commands/patches.py    |    8 ++++----
 stgit/commands/pick.py       |   18 +++++++++---------
 stgit/commands/pop.py        |    2 +-
 stgit/commands/pull.py       |    8 ++++----
 stgit/commands/push.py       |    6 +++---
 stgit/commands/rebase.py     |    2 +-
 stgit/commands/refresh.py    |   34 +++++++++++++++++-----------------
 stgit/commands/rename.py     |    2 +-
 stgit/commands/rm.py         |    2 +-
 stgit/commands/series.py     |   37 +++++++++++++++++--------------------
 stgit/commands/show.py       |    6 +++---
 stgit/commands/sink.py       |   10 +++++-----
 stgit/commands/sync.py       |   17 ++++++++---------
 stgit/commands/top.py        |    2 +-
 stgit/commands/unapplied.py  |    2 +-
 stgit/commands/uncommit.py   |   18 +++++++++---------
 stgit/commands/unhide.py     |    6 +++---
 stgit/main.py                |    7 -------
 37 files changed, 175 insertions(+), 186 deletions(-)
diff --git a/stgit/commands/add.py b/stgit/commands/add.py
index cc9dab0..48fb78a 100644
--- a/stgit/commands/add.py
+++ b/stgit/commands/add.py
@@ -39,7 +39,7 @@ class concreteCommand(Command):
         if len(self.args) < 1:
             self.parser.error('incorrect number of arguments')
 
-        if not crt_series.get_current():
+        if not self.current_head().get_current():
             raise CmdException, 'No patches applied'
 
         git.add(self.args)
diff --git a/stgit/commands/applied.py b/stgit/commands/applied.py
index a30a0e0..27077d9 100644
--- a/stgit/commands/applied.py
+++ b/stgit/commands/applied.py
@@ -45,7 +45,7 @@ class concreteCommand(Command):
         if len(self.args) != 0:
             self.parser.error('incorrect number of arguments')
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
 
         if self.flags.count:
             out.stdout(len(applied))
diff --git a/stgit/commands/assimilate.py b/stgit/commands/assimilate.py
index c85ae6e..b79358a 100644
--- a/stgit/commands/assimilate.py
+++ b/stgit/commands/assimilate.py
@@ -43,7 +43,7 @@ class concreteCommand(Command):
         def nothing_to_do():
             out.info('No commits to assimilate')
 
-        top_patch = crt_series.get_current_patch()
+        top_patch = self.current_head().get_current_patch()
         if not top_patch:
             return nothing_to_do()
 
@@ -61,7 +61,7 @@ class concreteCommand(Command):
         if not victims:
             return nothing_to_do()
 
-        if crt_series.get_protected():
+        if self.current_head().get_protected():
             raise CmdException(
                 'This branch is protected. Modification is not permitted')
 
@@ -69,7 +69,7 @@ class concreteCommand(Command):
         name2patch = {}
 
         def name_taken(name):
-            return name in name2patch or crt_series.patch_exists(name)
+            return name in name2patch or self.current_head().patch_exists(name)
 
         for victim in victims:
             patchname = make_patch_name(victim.get_log(), name_taken)
@@ -82,7 +82,7 @@ class concreteCommand(Command):
                      % (patch2name[victim], victim))
             aname, amail, adate = name_email_date(victim.get_author())
             cname, cmail, cdate = name_email_date(victim.get_committer())
-            crt_series.new_patch(
+            self.current_head().new_patch(
                 patch2name[victim],
                 can_edit = False, before_existing = False, refresh = False,
                 top = victim.get_id_hash(), bottom = victim.get_parent(),
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index e243830..ca4f98c 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -69,7 +69,7 @@ class concreteCommand(Command):
 
 
     def __is_current_branch(self, branch_name):
-        return crt_series.get_name() == branch_name
+        return self.current_head_name() == branch_name
 
     def __print_branch(self, branch_name, length):
         initialized = ' '
@@ -169,7 +169,7 @@ class concreteCommand(Command):
         elif self.flags.clone:
 
             if len(self.args) == 0:
-                clone = crt_series.get_name() + \
+                clone = self.current_head_name() + \
                         time.strftime('-%C%y%m%d-%H%M%S')
             elif len(self.args) == 1:
                 clone = self.args[0]
@@ -181,7 +181,7 @@ class concreteCommand(Command):
             self.check_head_top_equal()
 
             out.start('Cloning current branch to "%s"' % clone)
-            crt_series.clone(clone)
+            self.current_head().clone(clone)
             out.done()
 
             return
@@ -216,7 +216,7 @@ class concreteCommand(Command):
         elif self.flags.protect:
 
             if len(self.args) == 0:
-                branch_name = crt_series.get_name()
+                branch_name = self.current_head_name()
             elif len(self.args) == 1:
                 branch_name = self.args[0]
             else:
@@ -250,7 +250,7 @@ class concreteCommand(Command):
         elif self.flags.unprotect:
 
             if len(self.args) == 0:
-                branch_name = crt_series.get_name()
+                branch_name = self.current_head_name()
             elif len(self.args) == 1:
                 branch_name = self.args[0]
             else:
@@ -270,7 +270,7 @@ class concreteCommand(Command):
         elif self.flags.description is not None:
 
             if len(self.args) == 0:
-                branch_name = crt_series.get_name()
+                branch_name = self.current_head_name()
             elif len(self.args) == 1:
                 branch_name = self.args[0]
             else:
@@ -304,4 +304,4 @@ class concreteCommand(Command):
         if len(self.args) != 0:
             self.parser.error('incorrect number of arguments')
 
-        print crt_series.get_name()
+        print self.current_head_name()
diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index 3e9d829..74f5cf7 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -43,14 +43,14 @@ class concreteCommand(Command):
         """Delete the empty patches
         """
         for p in patches:
-            if crt_series.empty_patch(p):
+            if self.current_head().empty_patch(p):
                 out.start('Deleting patch "%s"' % p)
-                if applied and crt_series.patch_applied(p):
-                    crt_series.pop_patch(p)
-                crt_series.delete_patch(p)
+                if applied and self.current_head().patch_applied(p):
+                    self.current_head().pop_patch(p)
+                self.current_head().delete_patch(p)
                 out.done()
-            elif applied and crt_series.patch_unapplied(p):
-                crt_series.push_patch(p)
+            elif applied and self.current_head().patch_unapplied(p):
+                self.current_head().push_patch(p)
 
     def func(self):
         """Delete the empty patches in the series
@@ -66,11 +66,11 @@ class concreteCommand(Command):
             self.flags.applied = self.flags.unapplied = True
 
         if self.flags.applied:
-            applied = crt_series.get_applied()
+            applied = self.current_head().get_applied()
             self.__delete_empty(applied, True)
 
         if self.flags.unapplied:
-            unapplied = crt_series.get_unapplied()
+            unapplied = self.current_head().get_unapplied()
             self.__delete_empty(unapplied, False)
 
         self.print_crt_patch()
diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py
index 56f3943..decba21 100644
--- a/stgit/commands/commit.py
+++ b/stgit/commands/commit.py
@@ -44,21 +44,21 @@ class concreteCommand(Command):
         check_conflicts()
         self.check_head_top_equal()
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
         if not applied:
             raise CmdException, 'No patches applied'
 
-        if crt_series.get_protected():
+        if self.current_head().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])
+        self.current_head().pop_patch(applied[0])
         git.switch(crt_head)
 
         for patch in applied:
-            crt_series.delete_patch(patch)
+            self.current_head().delete_patch(patch)
 
         out.done()
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index 1ed5086..12487a5 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -25,9 +25,6 @@ from stgit.utils import *
 from stgit import stack, git, basedir
 from stgit.config import config, file_extensions
 
-crt_series = None
-
-
 # Command exception class
 class CmdException(Exception):
     pass
@@ -48,6 +45,7 @@ class Command:
     This command is undocumented."""
     options = []
     __head_name = None
+    __head = None
 
     def __init__(self, name):
         self.__name = name
@@ -61,7 +59,9 @@ class Command:
         return self.__head_name
 
     def current_head(self):
-        return stack.Series(self.current_head_name())
+        if not self.__head:
+            self.__head = stack.Series(self.current_head_name())
+        return self.__head
 
     def func(self):
         raise CmdException, 'Unimplemented command'
@@ -79,7 +79,7 @@ class Command:
         try:
             patch, branch, patch_id = parse_rev(rev)
             if branch == None:
-                series = crt_series
+                series = self.current_head()
             else:
                 series = stack.Series(branch)
             if patch == None:
@@ -104,7 +104,7 @@ class Command:
         return git.rev_parse(rev + '^{commit}')
 
     def check_head_top_equal(self):
-        if not crt_series.head_top_equal():
+        if not self.current_head().head_top_equal():
             raise CmdException(
                 'HEAD and top are not the same. You probably committed\n'
                 '  changes to the tree outside of StGIT. To bring them\n'
@@ -112,7 +112,7 @@ class Command:
 
     def print_crt_patch(self, branch = None):
         if not branch:
-            patch = crt_series.get_current()
+            patch = self.current_head().get_current()
         else:
             patch = stack.Series(branch).get_current()
 
@@ -125,7 +125,7 @@ class Command:
         """Push multiple patches onto the stack. This function is shared
         between the push and pull commands
         """
-        forwarded = crt_series.forward_patches(patches)
+        forwarded = self.current_head().forward_patches(patches)
         if forwarded > 1:
             out.info('Fast-forwarded patches "%s" - "%s"'
                      % (patches[0], patches[forwarded - 1]))
@@ -138,7 +138,7 @@ class Command:
         if names and check_merged:
             out.start('Checking for patches merged upstream')
 
-            merged = crt_series.merged_patches(names)
+            merged = self.current_head().merged_patches(names)
 
             out.done('%d found' % len(merged))
         else:
@@ -148,12 +148,12 @@ class Command:
             out.start('Pushing patch "%s"' % p)
 
             if p in merged:
-                crt_series.push_patch(p, empty = True)
+                self.current_head().push_patch(p, empty = True)
                 out.done('merged upstream')
             else:
-                modified = crt_series.push_patch(p)
+                modified = self.current_head().push_patch(p)
 
-                if crt_series.empty_patch(p):
+                if self.current_head().empty_patch(p):
                     out.done('empty patch')
                 elif modified:
                     out.done('modified')
@@ -172,22 +172,22 @@ class Command:
                 out.start('Popping patch "%s"' % p)
             else:
                 out.start('Popping patches "%s" - "%s"' % (patches[0], p))
-            crt_series.pop_patch(p, keep)
+            self.current_head().pop_patch(p, keep)
             out.done()
 
     def prepare_rebase(self, force=None):
         if not force:
             # Be sure we won't loose results of stg-(un)commit by error.
             # Do not require an existing orig-base for compatibility with 0.12 and earlier.
-            origbase = crt_series._get_field('orig-base')
-            if origbase and crt_series.get_base() != origbase:
+            origbase = self.current_head()._get_field('orig-base')
+            if origbase and self.current_head().get_base() != origbase:
                 raise CmdException, 'Rebasing would possibly lose data'
 
         # pop all patches
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
         if len(applied) > 0:
             out.start('Popping all applied patches')
-            crt_series.pop_patch(applied[0])
+            self.current_head().pop_patch(applied[0])
             out.done()
         return applied
 
@@ -201,7 +201,7 @@ class Command:
 
     def post_rebase(self, applied, nopush, merged):
         # memorize that we rebased to here
-        crt_series._set_field('orig-base', git.get_head())
+        self.current_head()._set_field('orig-base', git.get_head())
         # push the patches back
         if not nopush:
             self.push_patches(applied, merged)
diff --git a/stgit/commands/copy.py b/stgit/commands/copy.py
index 393d509..3eb267e 100644
--- a/stgit/commands/copy.py
+++ b/stgit/commands/copy.py
@@ -37,7 +37,7 @@ class concreteCommand(Command):
         if len(self.args) < 1:
             self.parser.error('incorrect number of arguments')
 
-        if not crt_series.get_current():
+        if not self.current_head().get_current():
             raise CmdException, 'No patches applied'
 
         git.copy(self.args[0:-1], self.args[-1])
diff --git a/stgit/commands/delete.py b/stgit/commands/delete.py
index 6d10895..b87d13d 100644
--- a/stgit/commands/delete.py
+++ b/stgit/commands/delete.py
@@ -42,8 +42,8 @@ class concreteCommand(Command):
     def func(self):
         """Deletes one or more patches.
         """
-        applied_patches = crt_series.get_applied()
-        unapplied_patches = crt_series.get_unapplied()
+        applied_patches = self.current_head().get_applied()
+        unapplied_patches = self.current_head().get_unapplied()
         all_patches = applied_patches + unapplied_patches
 
         if self.args:
@@ -74,7 +74,7 @@ class concreteCommand(Command):
 
         # delete the patches
         for patch in applied + patches:
-            crt_series.delete_patch(patch)
+            self.current_head().delete_patch(patch)
             out.info('Patch "%s" successfully deleted' % patch)
 
         if not self.flags.branch:
diff --git a/stgit/commands/export.py b/stgit/commands/export.py
index fb5e87b..f1c1920 100644
--- a/stgit/commands/export.py
+++ b/stgit/commands/export.py
@@ -76,7 +76,7 @@ class concreteCommand(Command):
         if self.flags.dir:
             dirname = self.flags.dir
         else:
-            dirname = 'patches-%s' % crt_series.get_name()
+            dirname = 'patches-%s' % self.current_head_name()
 
         if not self.flags.branch and git.local_changes():
             out.warn('Local changes in the tree;'
@@ -92,7 +92,7 @@ class concreteCommand(Command):
         else:
             diff_flags = []
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
         if len(self.args) != 0:
             patches = parse_patches(self.args, applied)
         else:
@@ -116,7 +116,7 @@ class concreteCommand(Command):
 
         # note the base commit for this series
         if not self.flags.stdout:
-            base_commit = crt_series.get_patch(patches[0]).get_bottom()
+            base_commit = self.current_head().get_patch(patches[0]).get_bottom()
             print >> series, '# This series applies on GIT commit %s' % base_commit
 
         patch_no = 1;
@@ -133,7 +133,7 @@ class concreteCommand(Command):
                 print >> series, pname
 
             # get the patch description
-            patch = crt_series.get_patch(p)
+            patch = self.current_head().get_patch(p)
 
             descr = patch.get_description().strip()
             descr_lines = descr.split('\n')
diff --git a/stgit/commands/float.py b/stgit/commands/float.py
index ea82ea5..b3b9acb 100644
--- a/stgit/commands/float.py
+++ b/stgit/commands/float.py
@@ -48,8 +48,8 @@ class concreteCommand(Command):
         check_conflicts()
         self.check_head_top_equal()
 
-        unapplied = crt_series.get_unapplied()
-        applied = crt_series.get_applied()
+        unapplied = self.current_head().get_unapplied()
+        applied = self.current_head().get_applied()
         all = unapplied + applied
 
         if self.flags.series:
diff --git a/stgit/commands/fold.py b/stgit/commands/fold.py
index 6d75df5..734db43 100644
--- a/stgit/commands/fold.py
+++ b/stgit/commands/fold.py
@@ -56,7 +56,7 @@ class concreteCommand(Command):
         else:
             filename = None
 
-        current = crt_series.get_current()
+        current = self.current_head().get_current()
         if not current:
             raise CmdException, 'No patches applied'
 
@@ -69,7 +69,7 @@ class concreteCommand(Command):
             out.start('Folding patch from stdin')
 
         if self.flags.threeway:
-            crt_patch = crt_series.get_patch(current)
+            crt_patch = self.current_head().get_patch(current)
             bottom = crt_patch.get_bottom()
             git.apply_patch(filename = filename, base = bottom)
         elif self.flags.base:
diff --git a/stgit/commands/goto.py b/stgit/commands/goto.py
index ceec9ea..dc862ef 100644
--- a/stgit/commands/goto.py
+++ b/stgit/commands/goto.py
@@ -45,8 +45,8 @@ class concreteCommand(Command):
         check_conflicts()
         self.check_head_top_equal()
 
-        applied = crt_series.get_applied()
-        unapplied = crt_series.get_unapplied()
+        applied = self.current_head().get_applied()
+        unapplied = self.current_head().get_unapplied()
         patch = self.args[0]
 
         if patch in applied:
diff --git a/stgit/commands/hide.py b/stgit/commands/hide.py
index 4259fdf..ef17592 100644
--- a/stgit/commands/hide.py
+++ b/stgit/commands/hide.py
@@ -38,12 +38,12 @@ class concreteCommand(Command):
         """
         if self.args:
             # parsing all the patches for a more meaningful error reporting
-            all_patches = crt_series.get_applied() + crt_series.get_unapplied() \
-                          + crt_series.get_hidden()
+            all_patches = self.current_head().get_applied() + self.current_head().get_unapplied() \
+                          + self.current_head().get_hidden()
             patches = parse_patches(self.args, all_patches)
         else:
             self.parser.error('No patches specified')
 
         for patch in patches:
-            crt_series.hide_patch(patch)
+            self.current_head().hide_patch(patch)
             out.info('Patch "%s" hidden' % patch)
diff --git a/stgit/commands/imprt.py b/stgit/commands/imprt.py
index 2401c4a..b45ec29 100644
--- a/stgit/commands/imprt.py
+++ b/stgit/commands/imprt.py
@@ -246,7 +246,7 @@ class concreteCommand(Command):
             if self.flags.ignore or self.flags.replace:
                 unacceptable_name = lambda name: False
             else:
-                unacceptable_name = crt_series.patch_exists
+                unacceptable_name = self.current_head().patch_exists
             patch = make_patch_name(message, unacceptable_name)
         else:
             # fix possible invalid characters in the patch name
@@ -255,11 +255,11 @@ class concreteCommand(Command):
         if not diff:
             raise CmdException, 'No diff found inside the patch'
 
-        if self.flags.ignore and patch in crt_series.get_applied():
+        if self.flags.ignore and patch in self.current_head().get_applied():
             out.info('Ignoring already applied patch "%s"' % patch)
             return
-        if self.flags.replace and patch in crt_series.get_unapplied():
-            crt_series.delete_patch(patch)
+        if self.flags.replace and patch in self.current_head().get_unapplied():
+            self.current_head().delete_patch(patch)
 
         # refresh_patch() will invoke the editor in this case, with correct
         # patch content
@@ -283,20 +283,20 @@ class concreteCommand(Command):
         if self.flags.commemail:
             committer_email = self.flags.commemail
 
-        crt_series.new_patch(patch, message = message, can_edit = False,
-                             author_name = author_name,
-                             author_email = author_email,
-                             author_date = author_date,
-                             committer_name = committer_name,
-                             committer_email = committer_email)
+        self.current_head().new_patch(patch, message = message, can_edit = False,
+                                      author_name = author_name,
+                                      author_email = author_email,
+                                      author_date = author_date,
+                                      committer_name = committer_name,
+                                      committer_email = committer_email)
 
         out.start('Importing patch "%s"' % patch)
         if self.flags.base:
             git.apply_patch(diff = diff, base = self.git_id(self.flags.base))
         else:
             git.apply_patch(diff = diff)
-        crt_series.refresh_patch(edit = self.flags.edit,
-                                 show_patch = self.flags.showpatch)
+        self.current_head().refresh_patch(edit = self.flags.edit,
+                                          show_patch = self.flags.showpatch)
         out.done()
 
     def __import_file(self, filename, patch = None):
@@ -332,7 +332,7 @@ class concreteCommand(Command):
     def __import_series(self, filename):
         """Import a series of patches
         """
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
 
         if filename:
             f = file(filename)
diff --git a/stgit/commands/init.py b/stgit/commands/init.py
index 9ffcaaa..bdccacd 100644
--- a/stgit/commands/init.py
+++ b/stgit/commands/init.py
@@ -37,4 +37,4 @@ class concreteCommand(Command):
         if len(self.args) != 0:
             self.parser.error('incorrect number of arguments')
 
-        crt_series.init()
+        self.current_head().init()
diff --git a/stgit/commands/log.py b/stgit/commands/log.py
index 6b85975..d314c9a 100644
--- a/stgit/commands/log.py
+++ b/stgit/commands/log.py
@@ -86,17 +86,17 @@ class concreteCommand(Command):
         """Show the patch changelog
         """
         if len(self.args) == 0:
-            name = crt_series.get_current()
+            name = self.current_head().get_current()
             if not name:
                 raise CmdException, 'No patches applied'
         elif len(self.args) == 1:
             name = self.args[0]
-            if not name in crt_series.get_applied() + crt_series.get_unapplied():
+            if not name in self.current_head().get_applied() + self.current_head().get_unapplied():
                 raise CmdException, 'Unknown patch "%s"' % name
         else:
             self.parser.error('incorrect number of arguments')
 
-        patch = crt_series.get_patch(name)
+        patch = self.current_head().get_patch(name)
 
         log = patch.get_log()
         if not log:
diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index cdc0e2a..258cc7f 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -350,7 +350,7 @@ class concreteCommand(Command):
     def __build_message(self, tmpl, patch, patch_nr, total_nr, msg_id, ref_id):
         """Build the message to be sent via SMTP
         """
-        p = crt_series.get_patch(patch)
+        p = self.current_head().get_patch(patch)
 
         descr = p.get_description().strip()
         descr_lines = descr.split('\n')
@@ -463,12 +463,12 @@ class concreteCommand(Command):
         """
         smtpserver = config.get('stgit.smtpserver')
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
 
         if self.flags.all:
             patches = applied
         elif len(self.args) >= 1:
-            unapplied = crt_series.get_unapplied()
+            unapplied = self.current_head().get_unapplied()
             patches = parse_patches(self.args, applied + unapplied, len(applied))
         else:
             raise CmdException, 'Incorrect options. Unknown patches to send'
diff --git a/stgit/commands/new.py b/stgit/commands/new.py
index 87f3874..4149c58 100644
--- a/stgit/commands/new.py
+++ b/stgit/commands/new.py
@@ -74,10 +74,10 @@ class concreteCommand(Command):
         if self.flags.author:
             self.flags.authname, self.flags.authemail = name_email(self.flags.author)
 
-        crt_series.new_patch(name, message = self.flags.message,
-                             show_patch = self.flags.showpatch,
-                             author_name = self.flags.authname,
-                             author_email = self.flags.authemail,
-                             author_date = self.flags.authdate,
-                             committer_name = self.flags.commname,
-                             committer_email = self.flags.commemail)
+        self.current_head().new_patch(name, message = self.flags.message,
+                                      show_patch = self.flags.showpatch,
+                                      author_name = self.flags.authname,
+                                      author_email = self.flags.authemail,
+                                      author_date = self.flags.authdate,
+                                      committer_name = self.flags.commname,
+                                      committer_email = self.flags.commemail)
diff --git a/stgit/commands/patches.py b/stgit/commands/patches.py
index 7bf8920..f4abea6 100644
--- a/stgit/commands/patches.py
+++ b/stgit/commands/patches.py
@@ -51,18 +51,18 @@ class concreteCommand(Command):
         if len(self.args) < 1:
             self.parser.error('incorrect number of arguments')
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
         if not applied:
             raise CmdException, 'No patches applied'
 
-        revs = git.modifying_revs(self.args, crt_series.get_base(),
-                                  crt_series.get_head())
+        revs = git.modifying_revs(self.args, self.current_head().get_base(),
+                                  self.current_head().get_head())
         revs.reverse()
 
         # build the patch/revision mapping
         rev_patch = dict()
         for name in applied:
-            patch = crt_series.get_patch(name)
+            patch = self.current_head().get_patch(name)
             rev_patch[patch.get_top()] = patch
 
         # print the patch names
diff --git a/stgit/commands/pick.py b/stgit/commands/pick.py
index 6ec8be3..0fb7282 100644
--- a/stgit/commands/pick.py
+++ b/stgit/commands/pick.py
@@ -68,7 +68,7 @@ class concreteCommand(Command):
         commit = git.Commit(commit_id)
 
         if self.flags.fold or self.flags.update:
-            if not crt_series.get_current():
+            if not self.current_head().get_current():
                 raise CmdException, 'No patches applied'
         else:
             patch_branch = commit_str.split('@')
@@ -117,11 +117,11 @@ class concreteCommand(Command):
 
             out.start('Importing commit %s' % commit_id)
 
-            newpatch = crt_series.new_patch(patchname, message = message, can_edit = False,
-                                            unapplied = True, bottom = bottom, top = top,
-                                            author_name = author_name,
-                                            author_email = author_email,
-                                            author_date = author_date)
+            newpatch = self.current_head().new_patch(patchname, message = message, can_edit = False,
+                                                     unapplied = True, bottom = bottom, top = top,
+                                                     author_name = author_name,
+                                                     author_email = author_email,
+                                                     author_date = author_date)
             # in case the patch name was automatically generated
             patchname = newpatch.get_name()
 
@@ -135,7 +135,7 @@ class concreteCommand(Command):
                     # commit_str to a git_id
                     refseries = Series(refbranchname)
                 else:
-                    refseries = crt_series
+                    refseries = self.current_head()
                 patch = refseries.get_patch(refpatchname)
                 if patch.get_log():
                     out.info("Log was %s" % newpatch.get_log())
@@ -146,11 +146,11 @@ class concreteCommand(Command):
                     out.info("No log for %s\n" % patchname)
 
             if not self.flags.unapplied:
-                modified = crt_series.push_patch(patchname)
+                modified = self.current_head().push_patch(patchname)
             else:
                 modified = False
 
-            if crt_series.empty_patch(patchname):
+            if self.current_head().empty_patch(patchname):
                 out.done('empty patch')
             elif modified:
                 out.done('modified')
diff --git a/stgit/commands/pop.py b/stgit/commands/pop.py
index 3b8b536..9a81c8a 100644
--- a/stgit/commands/pop.py
+++ b/stgit/commands/pop.py
@@ -56,7 +56,7 @@ class concreteCommand(Command):
         if not self.flags.keep:
             check_local_changes()
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
         if not applied:
             raise CmdException, 'No patches applied'
 
diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index 06d9aa9..0824443 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -51,7 +51,7 @@ class concreteCommand(Command):
     def func(self):
         """Pull the changes from a remote repository
         """
-        policy = config.get('branch.%s.stgit.pull-policy' % crt_series.get_name()) or \
+        policy = config.get('branch.%s.stgit.pull-policy' % self.current_head_name()) or \
                  config.get('stgit.pull-policy')
 
         if policy == 'rebase':
@@ -69,9 +69,9 @@ class concreteCommand(Command):
             if len(self.args) >= 1:
                 repository = self.args[0]
             else:
-                repository = crt_series.get_parent_remote()
+                repository = self.current_head().get_parent_remote()
 
-        if crt_series.get_protected():
+        if self.current_head().get_protected():
             raise CmdException, 'This branch is protected. Pulls are not permitted'
 
         check_local_changes()
@@ -98,7 +98,7 @@ class concreteCommand(Command):
             git.fetch(repository)
             self.rebase(git.fetch_head())
         elif policy == 'rebase':
-            self.rebase(crt_series.get_parent_branch())
+            self.rebase(self.current_head().get_parent_branch())
 
         self.post_rebase(applied, self.flags.nopush, self.flags.merged)
 
diff --git a/stgit/commands/push.py b/stgit/commands/push.py
index 0d41cad..10acbff 100644
--- a/stgit/commands/push.py
+++ b/stgit/commands/push.py
@@ -61,13 +61,13 @@ class concreteCommand(Command):
 
         # If --undo is passed, do the work and exit
         if self.flags.undo:
-            patch = crt_series.get_current()
+            patch = self.current_head().get_current()
             if not patch:
                 raise CmdException, 'No patch to undo'
 
             out.start('Undoing push of "%s"' % patch)
             resolved_all()
-            if crt_series.undo_push():
+            if self.current_head().undo_push():
                 out.done()
             else:
                 out.done('patch unchanged')
@@ -79,7 +79,7 @@ class concreteCommand(Command):
         check_conflicts()
         self.check_head_top_equal()
 
-        unapplied = crt_series.get_unapplied()
+        unapplied = self.current_head().get_unapplied()
         if not unapplied:
             raise CmdException, 'No more patches to push'
 
diff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py
index f953d6b..f14f244 100644
--- a/stgit/commands/rebase.py
+++ b/stgit/commands/rebase.py
@@ -46,7 +46,7 @@ class concreteCommand(Command):
         if len(self.args) != 1:
             self.parser.error('incorrect number of arguments')
 
-        if crt_series.get_protected():
+        if self.current_head().get_protected():
             raise CmdException, 'This branch is protected. Rebase is not permitted'
 
         check_local_changes()
diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py
index 90eb26b..4d6eac8 100644
--- a/stgit/commands/refresh.py
+++ b/stgit/commands/refresh.py
@@ -88,10 +88,10 @@ class concreteCommand(Command):
                 raise CmdException, \
                       'Only full refresh is available with the --patch option'
             patch = self.flags.patch
-            if not crt_series.patch_applied(patch):
+            if not self.current_head().patch_applied(patch):
                 raise CmdException, 'Patches "%s" not applied' % patch
         else:
-            patch = crt_series.get_current()
+            patch = self.current_head().get_current()
             if not patch:
                 raise CmdException, 'No patches applied'
 
@@ -100,7 +100,7 @@ class concreteCommand(Command):
 
         if self.flags.undo:
             out.start('Undoing the refresh of "%s"' % patch)
-            crt_series.undo_refresh()
+            self.current_head().undo_refresh()
             out.done()
             return
 
@@ -117,14 +117,14 @@ class concreteCommand(Command):
             sign_str = None
 
         if git.local_changes() \
-               or not crt_series.head_top_equal() \
+               or not self.current_head().head_top_equal() \
                or self.flags.edit or self.flags.message \
                or self.flags.authname or self.flags.authemail or self.flags.authdate \
                or self.flags.commname or self.flags.commemail \
                or self.flags.sign or self.flags.ack:
 
             if self.flags.patch:
-                applied = crt_series.get_applied()
+                applied = self.current_head().get_applied()
                 between = applied[:applied.index(patch):-1]
                 self.pop_patches(between, keep = True)
 
@@ -132,18 +132,18 @@ class concreteCommand(Command):
 
             if autoresolved == 'yes':
                 resolved_all()
-            crt_series.refresh_patch(files = self.args,
-                                     message = self.flags.message,
-                                     edit = self.flags.edit,
-                                     show_patch = self.flags.showpatch,
-                                     author_name = self.flags.authname,
-                                     author_email = self.flags.authemail,
-                                     author_date = self.flags.authdate,
-                                     committer_name = self.flags.commname,
-                                     committer_email = self.flags.commemail,
-                                     backup = True, sign_str = sign_str)
-
-            if crt_series.empty_patch(patch):
+            self.current_head().refresh_patch(files = self.args,
+                                              message = self.flags.message,
+                                              edit = self.flags.edit,
+                                              show_patch = self.flags.showpatch,
+                                              author_name = self.flags.authname,
+                                              author_email = self.flags.authemail,
+                                              author_date = self.flags.authdate,
+                                              committer_name = self.flags.commname,
+                                              committer_email = self.flags.commemail,
+                                              backup = True, sign_str = sign_str)
+
+            if self.current_head().empty_patch(patch):
                 out.done('empty patch')
             else:
                 out.done()
diff --git a/stgit/commands/rename.py b/stgit/commands/rename.py
index 5199a4a..8cf6cab 100644
--- a/stgit/commands/rename.py
+++ b/stgit/commands/rename.py
@@ -40,5 +40,5 @@ class concreteCommand(Command):
             self.parser.error('incorrect number of arguments')
 
         out.start('Renaming patch "%s" to "%s"' % (self.args[0], self.args[1]))
-        crt_series.rename_patch(self.args[0], self.args[1])
+        self.current_head().rename_patch(self.args[0], self.args[1])
         out.done()
diff --git a/stgit/commands/rm.py b/stgit/commands/rm.py
index 68714d0..35ca151 100644
--- a/stgit/commands/rm.py
+++ b/stgit/commands/rm.py
@@ -42,7 +42,7 @@ class concreteCommand(Command):
         if len(self.args) < 1:
             self.parser.error('incorrect number of arguments')
 
-        if not crt_series.get_current():
+        if not self.current_head().get_current():
             raise CmdException, 'No patches applied'
 
         git.rm(self.args, self.flags.force)
diff --git a/stgit/commands/series.py b/stgit/commands/series.py
index 2afa013..c9f0efc 100644
--- a/stgit/commands/series.py
+++ b/stgit/commands/series.py
@@ -74,7 +74,7 @@ class concreteCommand(Command):
     def __get_description(self, patch):
         """Extract and return a patch's short description
         """
-        p = crt_series.get_patch(patch)
+        p = self.current_head().get_patch(patch)
         descr = (p.get_description() or '').strip()
         descr_lines = descr.split('\n')
         return descr_lines[0].rstrip()
@@ -82,7 +82,7 @@ class concreteCommand(Command):
     def __get_author(self, patch):
         """Extract and return a patch's short description
         """
-        p = crt_series.get_patch(patch)
+        p = self.current_head().get_patch(patch)
         return p.get_authname();
 
     def __print_patch(self, patch, branch_str, prefix, empty_prefix, length):
@@ -90,7 +90,7 @@ class concreteCommand(Command):
         """
         if self.flags.noprefix:
             prefix = ''
-        elif self.flags.empty and crt_series.empty_patch(patch):
+        elif self.flags.empty and self.current_head().empty_patch(patch):
             prefix = empty_prefix
 
         patch_str = patch + branch_str
@@ -108,41 +108,38 @@ class concreteCommand(Command):
     def func(self):
         """Show the patch series
         """
-        global crt_series
 
         # current series patches
         if self.flags.invisible:
             applied = unapplied = []
-            hidden = crt_series.get_hidden()
+            hidden = self.current_head().get_hidden()
         elif self.flags.all:
-            applied = crt_series.get_applied()
-            unapplied = crt_series.get_unapplied()
-            hidden = crt_series.get_hidden()
+            applied = self.current_head().get_applied()
+            unapplied = self.current_head().get_unapplied()
+            hidden = self.current_head().get_hidden()
         else:
-            applied = crt_series.get_applied()
-            unapplied = crt_series.get_unapplied()
+            applied = self.current_head().get_applied()
+            unapplied = self.current_head().get_unapplied()
             hidden = []
 
         if self.flags.missing:
             # switch the series, the one specified with --missing should
             # become the current
-            cmp_series = crt_series
-            crt_series = stack.Series(self.flags.missing)
-            stgit.commands.common.crt_series = crt_series
+            miss_series = stack.Series(self.flags.missing)
 
             cmp_patches = applied + unapplied + hidden
 
             # new current series patches
             if self.flags.invisible:
                 applied = unapplied = []
-                hidden = crt_series.get_hidden()
+                hidden = miss_series.get_hidden()
             elif self.flags.all:
-                applied = crt_series.get_applied()
-                unapplied = crt_series.get_unapplied()
-                hidden = crt_series.get_hidden()
+                applied = miss_series.get_applied()
+                unapplied = miss_series.get_unapplied()
+                hidden = miss_series.get_hidden()
             else:
-                applied = crt_series.get_applied()
-                unapplied = crt_series.get_unapplied()
+                applied = miss_series.get_applied()
+                unapplied = miss_series.get_unapplied()
                 hidden = []
         else:
             cmp_patches = []
@@ -182,7 +179,7 @@ class concreteCommand(Command):
             return
 
         if self.flags.showbranch:
-            branch_str = '@' + crt_series.get_name()
+            branch_str = '@' + self.current_head_name()
         else:
             branch_str = ''
 
diff --git a/stgit/commands/show.py b/stgit/commands/show.py
index 5ff772f..4540f58 100644
--- a/stgit/commands/show.py
+++ b/stgit/commands/show.py
@@ -44,8 +44,8 @@ class concreteCommand(Command):
     def func(self):
         """Show commit log and diff
         """
-        applied = crt_series.get_applied()
-        unapplied = crt_series.get_unapplied()
+        applied = self.current_head().get_applied()
+        unapplied = self.current_head().get_unapplied()
 
         if self.flags.applied:
             patches = applied
@@ -55,7 +55,7 @@ class concreteCommand(Command):
             patches = ['HEAD']
         else:
             if len(self.args) == 1 and self.args[0].find('..') == -1 \
-                   and not crt_series.patch_exists(self.args[0]):
+                   and not self.current_head().patch_exists(self.args[0]):
                 # it might be just a commit id
                 patches = self.args
             else:
diff --git a/stgit/commands/sink.py b/stgit/commands/sink.py
index c3ef4d5..84d6bac 100644
--- a/stgit/commands/sink.py
+++ b/stgit/commands/sink.py
@@ -47,20 +47,20 @@ class concreteCommand(Command):
         check_conflicts()
         self.check_head_top_equal()
 
-        oldapplied = crt_series.get_applied()
-        unapplied = crt_series.get_unapplied()
+        oldapplied = self.current_head().get_applied()
+        unapplied = self.current_head().get_unapplied()
         all = unapplied + oldapplied
 
         if len(self.args) > 0:
             patches = parse_patches(self.args, all)
         else:
-            patches = [ crt_series.get_current() ]
+            patches = [ self.current_head().get_current() ]
 
-        crt_series.pop_patch(self.flags.to or oldapplied[0])
+        self.current_head().pop_patch(self.flags.to or oldapplied[0])
         self.push_patches(patches)
 
         if not self.flags.nopush:
-            newapplied = crt_series.get_applied()
+            newapplied = self.current_head().get_applied()
             def not_reapplied_yet(p):
                 return not p in newapplied
             self.push_patches(filter(not_reapplied_yet, oldapplied))
diff --git a/stgit/commands/sync.py b/stgit/commands/sync.py
index 2882391..18d7394 100644
--- a/stgit/commands/sync.py
+++ b/stgit/commands/sync.py
@@ -67,7 +67,6 @@ class concreteCommand(Command):
     def func(self):
         """Synchronise a range of patches
         """
-        global crt_series
 
         if self.flags.undo:
             if self.flags.ref_branch or self.flags.series:
@@ -75,15 +74,15 @@ class concreteCommand(Command):
                       '--undo cannot be specified with --ref-branch or --series'
             self.__check_all()
 
-            out.start('Undoing the sync of "%s"' % crt_series.get_current())
-            crt_series.undo_refresh()
+            out.start('Undoing the sync of "%s"' % self.current_head().get_current())
+            self.current_head().undo_refresh()
             git.reset()
             out.done()
             return
 
         if self.flags.ref_branch:
             remote_series = stack.Series(self.flags.ref_branch)
-            if self.flags.ref_branch == crt_series.get_name():
+            if self.flags.ref_branch == self.current_head_name():
                 raise CmdException, 'Cannot synchronise with the current branch'
             remote_patches = remote_series.get_applied()
 
@@ -108,14 +107,14 @@ class concreteCommand(Command):
         else:
             raise CmdException, 'No remote branch or series specified'
 
-        applied = crt_series.get_applied()
+        applied = self.current_head().get_applied()
 
         if self.flags.all:
             patches = applied
         elif len(self.args) != 0:
             patches = parse_patches(self.args, applied, ordered = True)
         elif applied:
-            patches = [crt_series.get_current()]
+            patches = [self.current_head().get_current()]
         else:
             self.parser.error('no patches applied')
 
@@ -144,7 +143,7 @@ class concreteCommand(Command):
             # the actual sync
             out.start('Synchronising "%s"' % p)
 
-            patch = crt_series.get_patch(p)
+            patch = self.current_head().get_patch(p)
             bottom = patch.get_bottom()
             top = patch.get_top()
 
@@ -159,8 +158,8 @@ class concreteCommand(Command):
             if git.local_changes(verbose = False):
                 # index (cache) already updated by the git merge. The
                 # backup information was already reset above
-                crt_series.refresh_patch(cache_update = False, backup = False,
-                                         log = 'sync')
+                self.current_head().refresh_patch(cache_update = False, backup = False,
+                                                  log = 'sync')
                 out.done('updated')
             else:
                 out.done()
diff --git a/stgit/commands/top.py b/stgit/commands/top.py
index 88c03e2..ae57387 100644
--- a/stgit/commands/top.py
+++ b/stgit/commands/top.py
@@ -40,7 +40,7 @@ class concreteCommand(Command):
         if len(self.args) != 0:
             self.parser.error('incorrect number of arguments')
 
-        name = crt_series.get_current()
+        name = self.current_head().get_current()
         if name:
             out.stdout(name)
         else:
diff --git a/stgit/commands/unapplied.py b/stgit/commands/unapplied.py
index 083ed07..a5aaddc 100644
--- a/stgit/commands/unapplied.py
+++ b/stgit/commands/unapplied.py
@@ -44,7 +44,7 @@ class concreteCommand(Command):
         if len(self.args) != 0:
             self.parser.error('incorrect number of arguments')
 
-        unapplied = crt_series.get_unapplied()
+        unapplied = self.current_head().get_unapplied()
 
         if self.flags.count:
             out.stdout(len(unapplied))
diff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py
index ee2342f..48fda15 100644
--- a/stgit/commands/uncommit.py
+++ b/stgit/commands/uncommit.py
@@ -84,7 +84,7 @@ class concreteCommand(Command):
             patchnames = self.args
             patch_nr = len(patchnames)
 
-        if crt_series.get_protected():
+        if self.current_head().get_protected():
             raise CmdException, \
                   'This branch is protected. Uncommit is not permitted'
 
@@ -98,7 +98,7 @@ class concreteCommand(Command):
             return (commit, commit_id, parent)
 
         commits = []
-        next_commit = crt_series.get_base()
+        next_commit = self.current_head().get_base()
         if patch_nr:
             out.start('Uncommitting %d patches' % patch_nr)
             for i in xrange(patch_nr):
@@ -119,12 +119,12 @@ class concreteCommand(Command):
             zip(commits, patchnames or [None for i in xrange(len(commits))]):
             author_name, author_email, author_date = \
                          name_email_date(commit.get_author())
-            crt_series.new_patch(patchname,
-                                 can_edit = False, before_existing = True,
-                                 top = commit_id, bottom = parent,
-                                 message = commit.get_log(),
-                                 author_name = author_name,
-                                 author_email = author_email,
-                                 author_date = author_date)
+            self.current_head().new_patch(patchname,
+                                          can_edit = False, before_existing = True,
+                                          top = commit_id, bottom = parent,
+                                          message = commit.get_log(),
+                                          author_name = author_name,
+                                          author_email = author_email,
+                                          author_date = author_date)
 
         out.done()
diff --git a/stgit/commands/unhide.py b/stgit/commands/unhide.py
index 01d4b18..f9817c3 100644
--- a/stgit/commands/unhide.py
+++ b/stgit/commands/unhide.py
@@ -38,12 +38,12 @@ class concreteCommand(Command):
         """
         if self.args:
             # parsing all the patches for a more meaningful error reporting
-            all_patches = crt_series.get_applied() + crt_series.get_unapplied() \
-                          + crt_series.get_hidden()
+            all_patches = self.current_head().get_applied() + self.current_head().get_unapplied() \
+                          + self.current_head().get_hidden()
             patches = parse_patches(self.args, all_patches)
         else:
             self.parser.error('No patches specified')
 
         for patch in patches:
-            crt_series.unhide_patch(patch)
+            self.current_head().unhide_patch(patch)
             out.info('Patch "%s" unhidden' % patch)
diff --git a/stgit/main.py b/stgit/main.py
index 1fd5df9..1871cd3 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -271,13 +271,6 @@ def main():
 
     try:
         config_setup()
-
-        # 'clone' doesn't expect an already initialised GIT tree. A Series
-        # object will be created after the GIT tree is cloned
-        if cmd != 'clone':
-            command_module.crt_series = command.current_head()
-            stgit.commands.common.crt_series = command_module.crt_series
-
         command.func()
     except (IOError, ParsingError, NoSectionError, CmdException,
             StackException, GitException, GitMergeException,

Re: [StGIT PATCH 0/9] Refactoring of command handling

From: Yann Dirson <hidden>
Date: 2016-06-15 22:43:17

On Sun, Jun 17, 2007 at 12:00:28AM +0200, Yann Dirson wrote:
      Refactor command definition with a Command class.
That patch (3/9, and the main patch in the series) was most probably
blocked from the list because of its size.  The whole series is anyway
available at http://repo.or.cz/w/stgit/ydirson.git (together with even
more unreleased patches - beware ;)

More generally, I'll try to --sign those patches in my stack that I
consider stable and have been (or are in the queue fo being) sent to
Catalin.

Best regards,
-- 
Yann
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help