{"thread":{"id":"2710","subject":"[PATCH 6/7] Add a \"--clone\" option to \"stg branch\"","startedAt":"2005-11-29T22:05:52Z","lastAt":"2005-12-01T21:47:53Z","messageCount":16,"participants":["Chuck Lever","Catalin Marinas"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"12951","messageId":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":null,"subject":"[PATCH 0/7]","fromName":"Chuck Lever","fromEmail":"cel@citi.umich.edu","sentAt":"2005-11-29T22:05:52Z","receivedAt":"2005-11-29T22:05:52Z","isPatch":true,"sender":{"key":"cel@citi.umich.edu","avatar":null},"body":"These are road-tested and ready for your review.\n\n+ export-saves-base       | Make \"stg export\" save the base commit in the output directory\n+ stg-in-subdirectories   | Use git-rev-parse to find the local GIT repository\n+ fix-branch-description  | Align branch descriptions in output of \"stg branch -l\"\n+ stg-series-description  | \"stg series\" option to show patch summary descriptions\n+ stg-series-short        | Add facility to print short list of patches around 'top'\n+ stg-branch-clone        | Add a \"--clone\" option to \"stg branch\"\n> series-directory        | Use a separate directory for patches under each branch subdir\n\nBefore 0.8, you might also consider addressing the patch authorship issues\nthat come up when mailing out patches, as discussed on git@vger last week.\n\nI've adjusted my patchmail.tmpl file as a workaround.\n\n        -- Chuck Lever\n--\ncorporate:    <cel at netapp dot com>\npersonal:     <chucklever at bigfoot dot com>\n"},{"id":"12954","messageId":"20051129220938.9885.37456.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 1/7] Make \"stg export\" save the base commit in the output directory","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:38Z","receivedAt":"2005-11-29T22:09:38Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"When trying to apply a series of diffs that was exported from an StGIT\nseries, it can be convenient to know exactly which base commit the\npatches apply to.  Save that commit in a file patchdir/base.\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/commands/export.py |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/stgit/commands/export.py b/stgit/commands/export.py\nindex 0ef7d07..167a8d3 100644\n--- a/stgit/commands/export.py\n+++ b/stgit/commands/export.py\n@@ -141,6 +141,10 @@ def func(parser, options, args):\n             tmpl = file(patch_tmpl).read()\n             break\n \n+    # note the base commit for this series\n+    write_string(os.path.join(dirname, 'base'), \\\n+                 crt_series.get_patch(patches[0]).get_bottom())\n+\n     patch_no = 1;\n     for p in patches:\n         pname = p\n"},{"id":"12949","messageId":"20051129220940.9885.55885.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 2/7] Use git-rev-parse to find the local GIT repository","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:40Z","receivedAt":"2005-11-29T22:09:40Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"Use the latest git-rev-parse technology to allow some StGIT commands to\nfunction correctly in subdirectories of the working directory.\n\nAny command that relies on git-read-tree still doesn't work (changes to\nGIT forthcoming).\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/commands/branch.py   |    2 +-\n stgit/commands/common.py   |    4 ++--\n stgit/commands/export.py   |    2 +-\n stgit/commands/mail.py     |    4 ++--\n stgit/commands/resolved.py |    4 ++--\n stgit/git.py               |   35 ++++++++++++++++++-----------------\n stgit/stack.py             |    9 +++++----\n 7 files changed, 31 insertions(+), 29 deletions(-)\n\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex 9bf6cdb..c3f7944 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -136,7 +136,7 @@ def func(parser, options, args):\n         if len(args) != 0:\n             parser.error('incorrect number of arguments')\n \n-        branches = os.listdir(os.path.join(git.base_dir, 'refs', 'heads'))\n+        branches = os.listdir(os.path.join(git.get_base_dir(), 'refs', 'heads'))\n         branches.sort()\n \n         print 'Available branches:'\ndiff --git a/stgit/commands/common.py b/stgit/commands/common.py\nindex e437111..8084cbd 100644\n--- a/stgit/commands/common.py\n+++ b/stgit/commands/common.py\n@@ -96,7 +96,7 @@ def check_head_top_equal():\n               '  are doing, use the \"refresh -f\" command'\n \n def check_conflicts():\n-    if os.path.exists(os.path.join(git.base_dir, 'conflicts')):\n+    if os.path.exists(os.path.join(git.get_base_dir(), 'conflicts')):\n         raise CmdException, 'Unsolved conflicts. Please resolve them first'\n \n def print_crt_patch(branch = None):\n@@ -130,7 +130,7 @@ def resolved_all(reset = None):\n     if conflicts:\n         for filename in conflicts:\n             resolved(filename, reset)\n-        os.remove(os.path.join(git.base_dir, 'conflicts'))\n+        os.remove(os.path.join(git.get_base_dir(), 'conflicts'))\n \n def name_email(address):\n     \"\"\"Return a tuple consisting of the name and email parsed from a\ndiff --git a/stgit/commands/export.py b/stgit/commands/export.py\nindex 167a8d3..c93ab6e 100644\n--- a/stgit/commands/export.py\n+++ b/stgit/commands/export.py\n@@ -132,7 +132,7 @@ def func(parser, options, args):\n     else:\n         patch_tmpl_list = []\n \n-    patch_tmpl_list += [os.path.join(git.base_dir, 'patchexport.tmpl'),\n+    patch_tmpl_list += [os.path.join(git.get_base_dir(), 'patchexport.tmpl'),\n                         os.path.join(sys.prefix,\n                                      'share/stgit/templates/patchexport.tmpl')]\n     tmpl = ''\ndiff --git a/stgit/commands/mail.py b/stgit/commands/mail.py\nindex 7cc18bc..b3b7b49 100644\n--- a/stgit/commands/mail.py\n+++ b/stgit/commands/mail.py\n@@ -419,7 +419,7 @@ def func(parser, options, args):\n         if options.cover:\n             tfile_list = [options.cover]\n         else:\n-            tfile_list = [os.path.join(git.base_dir, 'covermail.tmpl'),\n+            tfile_list = [os.path.join(git.get_base_dir(), 'covermail.tmpl'),\n                           os.path.join(sys.prefix,\n                                        'share/stgit/templates/covermail.tmpl')]\n \n@@ -450,7 +450,7 @@ def func(parser, options, args):\n     if options.template:\n         tfile_list = [options.template]\n     else:\n-        tfile_list = [os.path.join(git.base_dir, 'patchmail.tmpl'),\n+        tfile_list = [os.path.join(git.get_base_dir(), 'patchmail.tmpl'),\n                       os.path.join(sys.prefix,\n                                    'share/stgit/templates/patchmail.tmpl')]\n     tmpl = None\ndiff --git a/stgit/commands/resolved.py b/stgit/commands/resolved.py\nindex d21ecc9..585c51b 100644\n--- a/stgit/commands/resolved.py\n+++ b/stgit/commands/resolved.py\n@@ -65,8 +65,8 @@ def func(parser, options, args):\n \n     # save or remove the conflicts file\n     if conflicts == []:\n-        os.remove(os.path.join(git.base_dir, 'conflicts'))\n+        os.remove(os.path.join(git.get_base_dir(), 'conflicts'))\n     else:\n-        f = file(os.path.join(git.base_dir, 'conflicts'), 'w+')\n+        f = file(os.path.join(git.get_base_dir(), 'conflicts'), 'w+')\n         f.writelines([line + '\\n' for line in conflicts])\n         f.close()\ndiff --git a/stgit/git.py b/stgit/git.py\nindex b19f75f..2cedeaa 100644\n--- a/stgit/git.py\n+++ b/stgit/git.py\n@@ -27,12 +27,6 @@ class GitException(Exception):\n     pass\n \n \n-# Different start-up variables read from the environment\n-if 'GIT_DIR' in os.environ:\n-    base_dir = os.environ['GIT_DIR']\n-else:\n-    base_dir = '.git'\n-\n \n #\n # Classes\n@@ -87,6 +81,15 @@ __commits = dict()\n #\n # Functions\n #\n+\n+def get_base_dir():\n+    \"\"\"Different start-up variables read from the environment\n+    \"\"\"\n+    if 'GIT_DIR' in os.environ:\n+        return os.environ['GIT_DIR']\n+    else:\n+        return _output_one_line('git-rev-parse --git-dir')\n+\n def get_commit(id_hash):\n     \"\"\"Commit objects factory. Save/look-up them in the __commits\n     dictionary\n@@ -103,7 +106,7 @@ def get_commit(id_hash):\n def get_conflicts():\n     \"\"\"Return the list of file conflicts\n     \"\"\"\n-    conflicts_file = os.path.join(base_dir, 'conflicts')\n+    conflicts_file = os.path.join(get_base_dir(), 'conflicts')\n     if os.path.isfile(conflicts_file):\n         f = file(conflicts_file)\n         names = [line.strip() for line in f.readlines()]\n@@ -167,9 +170,6 @@ def __run(cmd, args=None):\n         return r\n     return 0\n \n-def __check_base_dir():\n-    return os.path.isdir(base_dir)\n-\n def __tree_status(files = None, tree_id = 'HEAD', unknown = False,\n                   noexclude = True):\n     \"\"\"Returns a list of pairs - [status, filename]\n@@ -182,7 +182,7 @@ def __tree_status(files = None, tree_id \n \n     # unknown files\n     if unknown:\n-        exclude_file = os.path.join(base_dir, 'info', 'exclude')\n+        exclude_file = os.path.join(get_base_dir(), 'info', 'exclude')\n         base_exclude = ['--exclude=%s' % s for s in\n                         ['*.[ao]', '*.pyc', '.*', '*~', '#*', 'TAGS', 'tags']]\n         base_exclude.append('--exclude-per-directory=.gitignore')\n@@ -296,8 +296,8 @@ def create_branch(new_branch, tree_id = \n     if tree_id:\n         switch(tree_id)\n \n-    if os.path.isfile(os.path.join(base_dir, 'MERGE_HEAD')):\n-        os.remove(os.path.join(base_dir, 'MERGE_HEAD'))\n+    if os.path.isfile(os.path.join(get_base_dir(), 'MERGE_HEAD')):\n+        os.remove(os.path.join(get_base_dir(), 'MERGE_HEAD'))\n \n def switch_branch(name):\n     \"\"\"Switch to a git branch\n@@ -316,8 +316,8 @@ def switch_branch(name):\n         __head = tree_id\n     set_head_file(new_head)\n \n-    if os.path.isfile(os.path.join(base_dir, 'MERGE_HEAD')):\n-        os.remove(os.path.join(base_dir, 'MERGE_HEAD'))\n+    if os.path.isfile(os.path.join(get_base_dir(), 'MERGE_HEAD')):\n+        os.remove(os.path.join(get_base_dir(), 'MERGE_HEAD'))\n \n def delete_branch(name):\n     \"\"\"Delete a git branch\n@@ -325,7 +325,7 @@ def delete_branch(name):\n     branch_head = os.path.join('refs', 'heads', name)\n     if not branch_exists(branch_head):\n         raise GitException, 'Branch \"%s\" does not exist' % name\n-    os.remove(os.path.join(base_dir, branch_head))\n+    os.remove(os.path.join(get_base_dir(), branch_head))\n \n def rename_branch(from_name, to_name):\n     \"\"\"Rename a git branch\n@@ -339,7 +339,8 @@ def rename_branch(from_name, to_name):\n \n     if get_head_file() == from_name:\n         set_head_file(to_head)\n-    os.rename(os.path.join(base_dir, from_head), os.path.join(base_dir, to_head))\n+    os.rename(os.path.join(get_base_dir(), from_head), \\\n+              os.path.join(get_base_dir(), to_head))\n \n def add(names):\n     \"\"\"Add the files or recursively add the directory contents\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex 18b4c6e..dc7c19f 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -66,7 +66,7 @@ def __clean_comments(f):\n \n def edit_file(series, line, comment, show_patch = True):\n     fname = '.stgit.msg'\n-    tmpl = os.path.join(git.base_dir, 'patchdescr.tmpl')\n+    tmpl = os.path.join(git.get_base_dir(), 'patchdescr.tmpl')\n \n     f = file(fname, 'w+')\n     if line:\n@@ -263,9 +263,10 @@ class Series:\n             self.__name = git.get_head_file()\n \n         if self.__name:\n-            self.__patch_dir = os.path.join(git.base_dir, 'patches',\n+            base_dir = git.get_base_dir()\n+            self.__patch_dir = os.path.join(base_dir, 'patches',\n                                             self.__name)\n-            self.__base_file = os.path.join(git.base_dir, 'refs', 'bases',\n+            self.__base_file = os.path.join(base_dir, 'refs', 'bases',\n                                             self.__name)\n             self.__applied_file = os.path.join(self.__patch_dir, 'applied')\n             self.__unapplied_file = os.path.join(self.__patch_dir, 'unapplied')\n@@ -386,7 +387,7 @@ class Series:\n     def init(self):\n         \"\"\"Initialises the stgit series\n         \"\"\"\n-        bases_dir = os.path.join(git.base_dir, 'refs', 'bases')\n+        bases_dir = os.path.join(git.get_base_dir(), 'refs', 'bases')\n \n         if self.is_initialised():\n             raise StackException, self.__patch_dir + ' already exists'\n"},{"id":"12953","messageId":"20051129220942.9885.29688.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 3/7] Align branch descriptions in output of \"stg branch -l\"","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:43Z","receivedAt":"2005-11-29T22:09:43Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"When printing branch descriptions, start descriptions in the same column.\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/commands/branch.py |    9 +++++----\n 1 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex c3f7944..ccf1f6b 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -63,7 +63,7 @@ options = [make_option('-c', '--create',\n def __is_current_branch(branch_name):\n     return crt_series.get_branch() == branch_name\n \n-def __print_branch(branch_name):\n+def __print_branch(branch_name, length):\n     initialized = ' '\n     current = ' '\n     protected = ' '\n@@ -76,8 +76,8 @@ def __print_branch(branch_name):\n         current = '>'\n     if branch.get_protected():\n         protected = 'p'\n-    print '%s %s%s\\t%s\\t%s' % (current, initialized, protected, branch_name, \\\n-                               branch.get_description())\n+    print current + ' ' + initialized + protected + '\\t' + \\\n+          branch_name.ljust(length) + '  | ' + branch.get_description()\n \n def __delete_branch(doomed_name, force = False):\n     doomed = stack.Series(doomed_name)\n@@ -138,10 +138,11 @@ def func(parser, options, args):\n \n         branches = os.listdir(os.path.join(git.get_base_dir(), 'refs', 'heads'))\n         branches.sort()\n+        max_len = max([len(i) for i in branches])\n \n         print 'Available branches:'\n         for i in branches:\n-            __print_branch(i)\n+            __print_branch(i, max_len)\n         return\n \n     elif options.protect:\n"},{"id":"12955","messageId":"20051129220945.9885.51164.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 4/7] \"stg series\" option to show patch summary descriptions","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:45Z","receivedAt":"2005-11-29T22:09:45Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"Optionally show each patch's short description when listing a series.\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/commands/series.py |   48 +++++++++++++++++++++++++++++++---------------\n 1 files changed, 32 insertions(+), 16 deletions(-)\n\ndiff --git a/stgit/commands/series.py b/stgit/commands/series.py\nindex 032b89e..a843307 100644\n--- a/stgit/commands/series.py\n+++ b/stgit/commands/series.py\n@@ -33,12 +33,31 @@ prefixed with a '>'. Empty patches are p\n \n options = [make_option('-b', '--branch',\n                        help = 'use BRANCH instead of the default one'),\n+           make_option('-d', '--description',\n+                       help = 'show a show description for each patch',\n+                       action = 'store_true'),\n            make_option('-e', '--empty',\n                        help = 'check whether patches are empty '\n                        '(much slower)',\n                        action = 'store_true') ]\n \n \n+def __get_description(patch):\n+    \"\"\"Extract and return a patch's short description\n+    \"\"\"\n+    p = crt_series.get_patch(patch)\n+    descr = p.get_description().strip()\n+    descr_lines = descr.split('\\n')\n+    return descr_lines[0].rstrip()\n+\n+def __print_patch(patch, prefix, empty_prefix, length, options):\n+    if options.empty and crt_series.empty_patch(patch):\n+        prefix = empty_prefix\n+    if options.description:\n+        print prefix + patch.ljust(length) + '  | ' + __get_description(patch)\n+    else:\n+        print prefix + patch\n+\n def func(parser, options, args):\n     \"\"\"Show the patch series\n     \"\"\"\n@@ -46,21 +65,18 @@ def func(parser, options, args):\n         parser.error('incorrect number of arguments')\n \n     applied = crt_series.get_applied()\n+    unapplied = crt_series.get_unapplied()\n+    patches = applied + unapplied\n+\n+    max_len = 0\n+    if len(patches) > 0:\n+        max_len = max([len(i) for i in patches])\n+\n     if len(applied) > 0:\n         for p in applied [0:-1]:\n-            if options.empty and crt_series.empty_patch(p):\n-                print '0', p\n-            else:\n-                print '+', p\n-        p = applied[-1]\n-\n-        if options.empty and crt_series.empty_patch(p):\n-            print '0>%s' % p\n-        else:\n-            print '> %s' % p\n-\n-    for p in crt_series.get_unapplied():\n-        if options.empty and crt_series.empty_patch(p):\n-            print '0', p\n-        else:\n-            print '-', p\n+            __print_patch(p, '+ ', '0 ', max_len, options)\n+\n+        __print_patch(applied[-1], '> ', '0>', max_len, options)\n+\n+    for p in unapplied:\n+        __print_patch(p, '- ', '0 ', max_len, options)\n"},{"id":"12952","messageId":"20051129220947.9885.63635.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 5/7] Add facility to print short list of patches around 'top'","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:47Z","receivedAt":"2005-11-29T22:09:47Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"When working in the middle of a very long series, I often find it useful\nto have a list of the patches right around the current patch.  Add an\noption to \"stg series\" called \"--short\" to provide this short list.\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/commands/series.py |   10 ++++++++++\n 1 files changed, 10 insertions(+), 0 deletions(-)\n\ndiff --git a/stgit/commands/series.py b/stgit/commands/series.py\nindex a843307..ec1aaaf 100644\n--- a/stgit/commands/series.py\n+++ b/stgit/commands/series.py\n@@ -39,6 +39,9 @@ options = [make_option('-b', '--branch',\n            make_option('-e', '--empty',\n                        help = 'check whether patches are empty '\n                        '(much slower)',\n+                       action = 'store_true'),\n+           make_option('-s', '--short',\n+                       help = 'list just the patches around the topmost patch',\n                        action = 'store_true') ]\n \n \n@@ -66,6 +69,13 @@ def func(parser, options, args):\n \n     applied = crt_series.get_applied()\n     unapplied = crt_series.get_unapplied()\n+\n+    if options.short:\n+        if len(applied) > 5:\n+            applied = applied[-6:]\n+        if len(unapplied) > 5:\n+            unapplied = unapplied[:5]\n+\n     patches = applied + unapplied\n \n     max_len = 0\n"},{"id":"12948","messageId":"20051129220949.9885.51533.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 6/7] Add a \"--clone\" option to \"stg branch\"","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:49Z","receivedAt":"2005-11-29T22:09:49Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"Cloning a branch means creating a new branch and copying all of the\noriginal branch's patches and its base to it.  Like creating a tag,\nbut this also preserves all the StGIT patches as well.\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/commands/branch.py |   28 +++++++++++++++++++++++++---\n stgit/stack.py           |   35 ++++++++++++++++++++++++++++++++++-\n 2 files changed, 59 insertions(+), 4 deletions(-)\n\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex ccf1f6b..5bc5e94 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -18,7 +18,7 @@ along with this program; if not, write t\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n \"\"\"\n \n-import sys, os\n+import sys, os, time\n from optparse import OptionParser, make_option\n \n from stgit.commands.common import *\n@@ -29,10 +29,10 @@ from stgit import stack, git\n help = 'manage development branches'\n usage = \"\"\"%prog [options] branch-name [commit-id]\n \n-Create, list, switch between, rename, or delete development branches\n+Create, clone, switch between, rename, or delete development branches\n within a git repository.  By default, a single branch called 'master'\n is always created in a new repository.  This subcommand allows you to\n-manage several patch series in the same repository.\n+manage several patch series in the same repository via GIT branches.\n \n When displaying the branches, the names can be prefixed with\n 's' (StGIT managed) or 'p' (protected).\"\"\"\n@@ -40,6 +40,9 @@ When displaying the branches, the names \n options = [make_option('-c', '--create',\n                        help = 'create a new development branch',\n                        action = 'store_true'),\n+           make_option('--clone',\n+                       help = 'copy the contents of a branch',\n+                       action = 'store_true'),\n            make_option('--delete',\n                        help = 'delete an existing development branch',\n                        action = 'store_true'),\n@@ -124,6 +127,25 @@ def func(parser, options, args):\n         print 'Branch \"%s\" created.' % args[0]\n         return\n \n+    elif options.clone:\n+\n+        if len(args) == 0:\n+            clone = crt_series.get_branch() + \\\n+                    time.strftime('-%C%y%m%d-%H%M%S')\n+        elif len(args) == 1:\n+            clone = args[0]\n+        else:\n+            parser.error('incorrect number of arguments')\n+\n+        check_local_changes()\n+        check_conflicts()\n+        check_head_top_equal()\n+\n+        print 'Cloning current branch to \"%s\"...' % clone\n+        crt_series.clone(clone)\n+        print 'done'\n+        return\n+\n     elif options.delete:\n \n         if len(args) != 1:\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex dc7c19f..2866121 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -18,7 +18,7 @@ along with this program; if not, write t\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n \"\"\"\n \n-import sys, os\n+import sys, os, shutil\n \n from stgit.utils import *\n from stgit import git\n@@ -420,6 +420,39 @@ class Series:\n \n         self.__init__(to_name)\n \n+    def clone(self, target_series):\n+        \"\"\"Clones a series\n+        \"\"\"\n+        base = read_string(self.get_base_file())\n+        git.create_branch(target_series, tree_id = base)\n+        Series(target_series).init()\n+\n+        new_series = Series(target_series)\n+\n+        if os.path.exists(self.__descr_file):\n+            shutil.copyfile(self.__descr_file, new_series.__descr_file)\n+\n+        for p in self.get_applied():\n+            patch = self.get_patch(p)\n+            new_series.new_patch(p, message = patch.get_description(),\n+                                 can_edit = False, unapplied = True,\n+                                 bottom = patch.get_bottom(),\n+                                 top = patch.get_top(),\n+                                 author_name = patch.get_authname(),\n+                                 author_email = patch.get_authemail(),\n+                                 author_date = patch.get_authdate())\n+            modified = new_series.push_patch(p)\n+\n+        for p in self.get_unapplied():\n+            patch = self.get_patch(p)\n+            new_series.new_patch(p, message = patch.get_description(),\n+                                 can_edit = False, unapplied = True,\n+                                 bottom = patch.get_bottom(),\n+                                 top = patch.get_top(),\n+                                 author_name = patch.get_authname(),\n+                                 author_email = patch.get_authemail(),\n+                                 author_date = patch.get_authdate())\n+\n     def delete(self, force = False):\n         \"\"\"Deletes an stgit series\n         \"\"\"\n"},{"id":"12950","messageId":"20051129220951.9885.59702.stgit@dexter.citi.umich.edu","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"[PATCH 7/7] Use a separate directory for patches under each branch subdir","fromName":"Chuck Lever","fromEmail":"cel@netapp.com","sentAt":"2005-11-29T22:09:52Z","receivedAt":"2005-11-29T22:09:52Z","isPatch":true,"sender":{"key":"cel@netapp.com","avatar":null},"body":"Currently you can't specify a patch name that matches the name of one of\nthe stgit special files under .git/patches/<branch-name>.  Let's use a\nnew subdirectory under .git/patches/<branch-name> to contain just the\npatch directories to remove this limitation.\n\nSigned-off-by: Chuck Lever <cel@netapp.com>\n---\n\n stgit/stack.py |   24 +++++++++++++++---------\n 1 files changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex 2866121..46b404c 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -264,14 +264,19 @@ class Series:\n \n         if self.__name:\n             base_dir = git.get_base_dir()\n-            self.__patch_dir = os.path.join(base_dir, 'patches',\n-                                            self.__name)\n             self.__base_file = os.path.join(base_dir, 'refs', 'bases',\n                                             self.__name)\n-            self.__applied_file = os.path.join(self.__patch_dir, 'applied')\n-            self.__unapplied_file = os.path.join(self.__patch_dir, 'unapplied')\n-            self.__current_file = os.path.join(self.__patch_dir, 'current')\n-            self.__descr_file = os.path.join(self.__patch_dir, 'description')\n+            self.__series_dir = os.path.join(base_dir, 'patches',\n+                                             self.__name)\n+\n+            self.__applied_file = os.path.join(self.__series_dir, 'applied')\n+            self.__unapplied_file = os.path.join(self.__series_dir, 'unapplied')\n+            self.__current_file = os.path.join(self.__series_dir, 'current')\n+            self.__descr_file = os.path.join(self.__series_dir, 'description')\n+\n+            self.__patch_dir = os.path.join(self.__series_dir, 'patches')\n+            if not os.path.isdir(self.__patch_dir):\n+                self.__patch_dir = self.__series_dir\n \n     def get_branch(self):\n         \"\"\"Return the branch name for the Series object\n@@ -323,15 +328,15 @@ class Series:\n         return self.__base_file\n \n     def get_protected(self):\n-        return os.path.isfile(os.path.join(self.__patch_dir, 'protected'))\n+        return os.path.isfile(os.path.join(self.__series_dir, 'protected'))\n \n     def protect(self):\n-        protect_file = os.path.join(self.__patch_dir, 'protected')\n+        protect_file = os.path.join(self.__series_dir, 'protected')\n         if not os.path.isfile(protect_file):\n             create_empty_file(protect_file)\n \n     def unprotect(self):\n-        protect_file = os.path.join(self.__patch_dir, 'protected')\n+        protect_file = os.path.join(self.__series_dir, 'protected')\n         if os.path.isfile(protect_file):\n             os.remove(protect_file)\n \n@@ -399,6 +404,7 @@ class Series:\n         create_empty_file(self.__applied_file)\n         create_empty_file(self.__unapplied_file)\n         create_empty_file(self.__descr_file)\n+        os.makedirs(os.path.join(self.__series_dir, 'patches'))\n         self.__begin_stack_check()\n \n     def rename(self, to_name):\n"},{"id":"13007","messageId":"b0943d9e0511301353p5b8a3b82i@mail.gmail.com","threadId":"2710","inReplyTo":"20051129220938.9885.37456.stgit@dexter.citi.umich.edu","subject":"Re: [PATCH 1/7] Make \"stg export\" save the base commit in the output directory","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-11-30T21:53:21Z","receivedAt":"2005-11-30T21:53:21Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"On 29/11/05, Chuck Lever <cel@netapp.com> wrote:\n> When trying to apply a series of diffs that was exported from an StGIT\n> series, it can be convenient to know exactly which base commit the\n> patches apply to.  Save that commit in a file patchdir/base.\n\nI would rather save it as a comment ('#' prefixed) in the series file.\n\n--\nCatalin\n"},{"id":"13011","messageId":"b0943d9e0511301423h2885d442r@mail.gmail.com","threadId":"2710","inReplyTo":"20051129220951.9885.59702.stgit@dexter.citi.umich.edu","subject":"Re: [PATCH 7/7] Use a separate directory for patches under each branch subdir","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-11-30T22:23:05Z","receivedAt":"2005-11-30T22:23:05Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"On 29/11/05, Chuck Lever <cel@netapp.com> wrote:\n> Currently you can't specify a patch name that matches the name of one of\n> the stgit special files under .git/patches/<branch-name>.  Let's use a\n> new subdirectory under .git/patches/<branch-name> to contain just the\n> patch directories to remove this limitation.\n\nThis patch doesn't fix the already created branches. It would be\nuseful to upgrade the branch structure automatically if the patches\ndirectory is not found.\n\n--\nCatalin\n"},{"id":"13013","messageId":"b0943d9e0511301427j28d64dd8j@mail.gmail.com","threadId":"2710","inReplyTo":"20051129220552.9885.41086.stgit@dexter.citi.umich.edu","subject":"Re: [PATCH 0/7]","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-11-30T22:27:36Z","receivedAt":"2005-11-30T22:27:36Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"On 29/11/05, Chuck Lever <cel@citi.umich.edu> wrote:\n> + stg-in-subdirectories   | Use git-rev-parse to find the local GIT repository\n> + fix-branch-description  | Align branch descriptions in output of \"stg branch -l\"\n> + stg-series-description  | \"stg series\" option to show patch summary descriptions\n> + stg-series-short        | Add facility to print short list of patches around 'top'\n\nI applied the above patches.\n\n> + stg-branch-clone        | Add a \"--clone\" option to \"stg branch\"\n\nI'll comment on this tomorrow since I need to look at it in more\ndetail. One thing I would prefer is to be able to specify a name for\nthe newly cloned branch rather than generating one by default (which\ncan stay as well, as long as a 2nd argument with the name is not\ngiven).\n\n> Before 0.8, you might also consider addressing the patch authorship issues\n> that come up when mailing out patches, as discussed on git@vger last week.\n\nYes, I will.\n\nThanks for the patches.\n\n--\nCatalin\n"},{"id":"13014","messageId":"438E2797.2020205@citi.umich.edu","threadId":"2710","inReplyTo":"b0943d9e0511301423h2885d442r@mail.gmail.com","subject":"Re: [PATCH 7/7] Use a separate directory for patches under each branch subdir","fromName":"Chuck Lever","fromEmail":"cel@citi.umich.edu","sentAt":"2005-11-30T22:28:39Z","receivedAt":"2005-11-30T22:28:39Z","isPatch":true,"sender":{"key":"cel@citi.umich.edu","avatar":null},"body":"Catalin Marinas wrote:\n> On 29/11/05, Chuck Lever <cel@netapp.com> wrote:\n> \n>>Currently you can't specify a patch name that matches the name of one of\n>>the stgit special files under .git/patches/<branch-name>.  Let's use a\n>>new subdirectory under .git/patches/<branch-name> to contain just the\n>>patch directories to remove this limitation.\n> \n> \n> This patch doesn't fix the already created branches. It would be\n> useful to upgrade the branch structure automatically if the patches\n> directory is not found.\n\nthe reason i didn't do that is because i wanted to allow older versions \nof StGIT to continue to work on existing repositories.\n\nwhat if i added a migration tool to allow a user to switch back and forth?\n\n\nbegin:vcard\nfn:Chuck Lever\nn:Lever;Charles\norg:Network Appliance, Incorporated;Linux NFS Client Development\nadr:535 West William Street, Suite 3100;;Center for Information Technology Integration;Ann Arbor;MI;48103-4943;USA\nemail;internet:cel@citi.umich.edu\ntitle:Member of Technical Staff\ntel;work:+1 734 763 4415\ntel;fax:+1 734 763 4434\ntel;home:+1 734 668 1089\nx-mozilla-html:FALSE\nurl:http://www.monkey.org/~cel/\nversion:2.1\nend:vcard\n\n"},{"id":"13015","messageId":"b0943d9e0511301432m10b25887r@mail.gmail.com","threadId":"2710","inReplyTo":"438E2797.2020205@citi.umich.edu","subject":"Re: [PATCH 7/7] Use a separate directory for patches under each branch subdir","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-11-30T22:32:50Z","receivedAt":"2005-11-30T22:32:50Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"On 30/11/05, Chuck Lever <cel@citi.umich.edu> wrote:\n> Catalin Marinas wrote:\n> > This patch doesn't fix the already created branches. It would be\n> > useful to upgrade the branch structure automatically if the patches\n> > directory is not found.\n>\n> the reason i didn't do that is because i wanted to allow older versions\n> of StGIT to continue to work on existing repositories.\n\nYou are right.\n\n> what if i added a migration tool to allow a user to switch back and forth?\n\nA separate stg command for this would be useful. Anyway since the\nrepository structure might change again in the future, I think it\nwould be better to have a version string saved somewhere in\n.git/patches/version for reference.\n\n--\nCatalin\n"},{"id":"13027","messageId":"438E65AD.4050807@citi.umich.edu","threadId":"2710","inReplyTo":"b0943d9e0511301432m10b25887r@mail.gmail.com","subject":"Re: [PATCH 7/7] Use a separate directory for patches under each branch subdir","fromName":"Chuck Lever","fromEmail":"cel@citi.umich.edu","sentAt":"2005-12-01T02:53:33Z","receivedAt":"2005-12-01T02:53:33Z","isPatch":true,"sender":{"key":"cel@citi.umich.edu","avatar":null},"body":"Catalin Marinas wrote:\n> A separate stg command for this would be useful. Anyway since the\n> repository structure might change again in the future, I think it\n> would be better to have a version string saved somewhere in\n> .git/patches/version for reference.\n\nnote that we have the same problem with .git/patches that this patch \nfixes in the .git/patches/<series> directories; namely, that if we add \nfiles in .git/patches, we can't have branches with the same names.\n\ni was thinking of perhaps creating a .git/stgit subdirectory that might \ncontain the config file, an stgit metadata version file, and perhaps any \nlocally modified copies of the template files.\n\n\nbegin:vcard\nfn:Chuck Lever\nn:Lever;Charles\norg:Network Appliance, Incorporated;Linux NFS Client Development\nadr:535 West William Street, Suite 3100;;Center for Information Technology Integration;Ann Arbor;MI;48103-4943;USA\nemail;internet:cel@citi.umich.edu\ntitle:Member of Technical Staff\ntel;work:+1 734 763-4415\ntel;fax:+1 734 763 4434\ntel;home:+1 734 668-1089\nx-mozilla-html:FALSE\nurl:http://www.monkey.org/~cel/\nversion:2.1\nend:vcard\n\n"},{"id":"13062","messageId":"b0943d9e0512011305v3299f27au@mail.gmail.com","threadId":"2710","inReplyTo":"20051129220949.9885.51533.stgit@dexter.citi.umich.edu","subject":"Re: [PATCH 6/7] Add a \"--clone\" option to \"stg branch\"","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-12-01T21:05:32Z","receivedAt":"2005-12-01T21:05:32Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"On 29/11/05, Chuck Lever <cel@netapp.com> wrote:\n> Cloning a branch means creating a new branch and copying all of the\n> original branch's patches and its base to it.  Like creating a tag,\n> but this also preserves all the StGIT patches as well.\n[...]\n> --- a/stgit/commands/branch.py\n> +++ b/stgit/commands/branch.py\n[...]\n> +        print 'Cloning current branch to \"%s\"...' % clone\n\nI would put a comma after this line and add sys.stdout.flush(). So\nthat 'done' is shown on the same line.\n\n[...]\n> --- a/stgit/stack.py\n> +++ b/stgit/stack.py\n[...]\n> +    def clone(self, target_series):\n> +        \"\"\"Clones a series\n> +        \"\"\"\n\nWouldn't it be easier to do a recursive copy at the directory level\n(.git/patches/<branch> into .git/patches/<clone> and\n.git/refs/bases/<branch> into .git/refs/bases/<clone>)? Is there\nanything that could go wrong if we do this?\n\n--\nCatalin\n"},{"id":"13065","messageId":"438F6F89.7010004@citi.umich.edu","threadId":"2710","inReplyTo":"b0943d9e0512011305v3299f27au@mail.gmail.com","subject":"Re: [PATCH 6/7] Add a \"--clone\" option to \"stg branch\"","fromName":"Chuck Lever","fromEmail":"cel@citi.umich.edu","sentAt":"2005-12-01T21:47:53Z","receivedAt":"2005-12-01T21:47:53Z","isPatch":true,"sender":{"key":"cel@citi.umich.edu","avatar":null},"body":"Catalin Marinas wrote:\n> On 29/11/05, Chuck Lever <cel@netapp.com> wrote:\n> \n>>Cloning a branch means creating a new branch and copying all of the\n>>original branch's patches and its base to it.  Like creating a tag,\n>>but this also preserves all the StGIT patches as well.\n> \n> [...]\n> \n>>--- a/stgit/commands/branch.py\n>>+++ b/stgit/commands/branch.py\n> \n> [...]\n> \n>>+        print 'Cloning current branch to \"%s\"...' % clone\n> \n> \n> I would put a comma after this line and add sys.stdout.flush(). So\n> that 'done' is shown on the same line.\n\nd'oh!  that's left over from when the Series.clone() method actually \nprinted the name of each patch as it was cloning.  fixed.\n\n> [...]\n> \n>>--- a/stgit/stack.py\n>>+++ b/stgit/stack.py\n> \n> [...]\n> \n>>+    def clone(self, target_series):\n>>+        \"\"\"Clones a series\n>>+        \"\"\"\n> \n> \n> Wouldn't it be easier to do a recursive copy at the directory level\n> (.git/patches/<branch> into .git/patches/<clone> and\n> .git/refs/bases/<branch> into .git/refs/bases/<clone>)? Is there\n> anything that could go wrong if we do this?\n\nwell, then you'd also clone any junk files that were left in there (for \nexample, development notes).\n\nalso, by doing this a patch at a time, it gives the ability to hook in \nadditional functionality in the future, in case we want to do something \nspecial to each newly cloned patch, like mechanically change the patch \nnames, or automatically add some annotation, for example.\n\nit also automatically converts an old-style branch (patches and series \ncontrol files in same directory) to a new-style branch (patches in \nseparate directory from series control files).\n\nit follows the same basic pattern of logic as \"stg pick\".  cloning a \nbranch is kind of like 'picking' all of it's patches into a new branch.\n\nbut finally (and most practically speaking), i hunted around for Python \ncode to copy a directory hierarchy, and interestingly enough it looks \nabout as complicated (or maybe more complicated) than the clone \nimplementation i finally came up with, which is only about 30 lines.  i \nsuppose we could just do a \"os.system('cp -r yada yada')\" but that's \njust lame  ;^)  and it only works on UNIX.\n\n\nbegin:vcard\nfn:Chuck Lever\nn:Lever;Charles\norg:Network Appliance, Incorporated;Open Source NFS Client Development\nadr:535 West William Street, Suite 3100;;Center for Information Technology Integration;Ann Arbor;MI;48103-4943;USA\nemail;internet:cel@citi.umich.edu\ntitle:Member of Technical Staff\ntel;work:+1 734 763 4415\ntel;fax:+1 734 763 4434\ntel;home:+1 734 668 1089\nx-mozilla-html:FALSE\nurl:http://www.monkey.org/~cel/\nversion:2.1\nend:vcard\n\n"}]}