Thread (7 messages) flat view 7 messages, 3 authors, 2016-06-15
STALE3734d

[PATCH] Handle branch names with slashes

From: Karl Hasselström <hidden>
Date: 2016-06-15 22:42:19
Subsystem: the rest · Maintainer: Linus Torvalds

Let StGIT grok branch names with slashes in them. It used to fall flat
on its face when confronted with them.

I think I've covered all, or at least most cases, but there are
probably some bugs left if you look hard enough.

Signed-off-by: Karl Hasselström <redacted>

---

 stgit/commands/branch.py |    6 +++-
 stgit/git.py             |   12 +++++--
 stgit/stack.py           |   42 ++++++++++---------------
 stgit/utils.py           |   77 ++++++++++++++++++++++++++++++++++++++++------
 4 files changed, 97 insertions(+), 40 deletions(-)
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index ef44349..d3e8a3c 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -173,7 +173,11 @@ def func(parser, options, args):
         if len(args) != 0:
             parser.error('incorrect number of arguments')
 
-        branches = os.listdir(os.path.join(git.get_base_dir(), 'refs', 'heads'))
+        branches = []
+        basepath = os.path.join(git.get_base_dir(), 'refs', 'heads')
+        for path, dirs, files in os.walk(basepath):
+            branches += [remove_leading_dir(basepath, os.path.join(path, f))
+                         for f in files]
         branches.sort()
         max_len = max([len(i) for i in branches])
 
diff --git a/stgit/git.py b/stgit/git.py
index 582e803..724b6fd 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -232,7 +232,8 @@ def get_head():
 def get_head_file():
     """Returns the name of the file pointed to by the HEAD link
     """
-    return os.path.basename(_output_one_line('git-symbolic-ref HEAD'))
+    return remove_leading_dir(os.path.join('refs', 'heads'),
+                              _output_one_line('git-symbolic-ref HEAD'))
 
 def set_head_file(ref):
     """Resets HEAD to point to a new ref
@@ -325,7 +326,9 @@ def delete_branch(name):
     branch_head = os.path.join('refs', 'heads', name)
     if not branch_exists(branch_head):
         raise GitException, 'Branch "%s" does not exist' % name
-    os.remove(os.path.join(get_base_dir(), branch_head))
+    base = get_base_dir()
+    rm_file_and_dirs(os.path.join(base, branch_head),
+                     os.path.join(base, 'refs', 'heads'))
 
 def rename_branch(from_name, to_name):
     """Rename a git branch
@@ -339,8 +342,9 @@ def rename_branch(from_name, to_name):
 
     if get_head_file() == from_name:
         set_head_file(to_head)
-    os.rename(os.path.join(get_base_dir(), from_head), \
-              os.path.join(get_base_dir(), to_head))
+    base = os.path.join(get_base_dir())
+    rename_dirs(os.path.join(base, from_head), os.path.join(base, to_head),
+                os.path.join(base, 'refs', 'heads'))
 
 def add(names):
     """Add the files or recursively add the directory contents
diff --git a/stgit/stack.py b/stgit/stack.py
index 556c40e..68a2936 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -406,7 +406,7 @@ class Series:
         """
         if len(self.get_applied()) == 0:
             head = git.get_head()
-            write_string(self.__base_file, head)
+            write_string(self.__base_file, head, mkdir = True)
 
     def __end_stack_check(self):
         """Remove .git/refs/heads/base if the stack is empty.
@@ -499,9 +499,11 @@ class Series:
         git.rename_branch(self.__name, to_name)
 
         if os.path.isdir(self.__series_dir):
-            os.rename(self.__series_dir, to_stack.__series_dir)
+            rename_dirs(self.__series_dir, to_stack.__series_dir,
+                        os.path.join(self.__base_dir, 'patches'))
         if os.path.exists(self.__base_file):
-            os.rename(self.__base_file, to_stack.__base_file)
+            rename_dirs(self.__base_file, to_stack.__base_file,
+                        os.path.join(self.__base_dir, 'refs', 'bases'))
 
         self.__init__(to_name)
 
@@ -543,29 +545,19 @@ class Series:
             for p in patches:
                 Patch(p, self.__patch_dir, self.__refs_dir).delete()
 
-            if os.path.exists(self.__applied_file):
-                os.remove(self.__applied_file)
-            if os.path.exists(self.__unapplied_file):
-                os.remove(self.__unapplied_file)
-            if os.path.exists(self.__current_file):
-                os.remove(self.__current_file)
-            if os.path.exists(self.__descr_file):
-                os.remove(self.__descr_file)
-            if not os.listdir(self.__patch_dir):
-                os.rmdir(self.__patch_dir)
-            else:
-                print 'Patch directory %s is not empty.' % self.__name
-            if not os.listdir(self.__series_dir):
-                os.rmdir(self.__series_dir)
-            else:
-                print 'Series directory %s is not empty.' % self.__name
-            if not os.listdir(self.__refs_dir):
-                os.rmdir(self.__refs_dir)
-            else:
-                print 'Refs directory %s is not empty.' % self.__refs_dir
+            for f in [self.__applied_file, self.__unapplied_file,
+                      self.__current_file, self.__descr_file]:
+                rm_if_exists(f)
+
+            for (d, n) in [(self.__patch_dir, 'Patch'),
+                           (self.__series_dir, 'Series'),
+                           (self.__refs_dir, 'Refs')]:
+                if os.path.isdir(d) and os.listdir(d):
+                    print '%s directory %s is not empty.' % (n, self.__name)
+                rmdir_while_empty(d, self.__base_dir)
 
-        if os.path.exists(self.__base_file):
-            os.remove(self.__base_file)
+        rm_if_exists(self.__base_file)
+        rmdir_while_empty(os.path.dirname(self.__base_file), self.__base_dir)
 
     def refresh_patch(self, files = None, message = None, edit = False,
                       show_patch = False,
diff --git a/stgit/utils.py b/stgit/utils.py
index 5749b3b..33c62be 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -18,6 +18,18 @@ along with this program; if not, write t
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
+import os.path
+
+def mkdir_file(filename, mode, mkdir):
+    """Opens filename with the given mode, creating the directory it's
+    in if it doesn't already exist and mkdir is true
+    """
+    if mkdir:
+        d = os.path.dirname(filename)
+        if not os.path.isdir(d):
+            os.makedirs(d)
+    return file(filename, mode)
+
 def read_string(filename, multiline = False):
     """Reads the first line from a file
     """
@@ -29,42 +41,87 @@ def read_string(filename, multiline = Fa
     f.close()
     return result
 
-def write_string(filename, line, multiline = False):
+def write_string(filename, line, multiline = False, mkdir = False):
     """Writes 'line' to file and truncates it
     """
-    f = file(filename, 'w+')
+    f = mkdir_file(filename, 'w+', mkdir)
     if multiline:
         f.write(line)
     else:
         print >> f, line
     f.close()
 
-def append_strings(filename, lines):
+def append_strings(filename, lines, mkdir = False):
     """Appends 'lines' sequence to file
     """
-    f = file(filename, 'a+')
+    f = mkdir_file(filename, 'a+', mkdir)
     for line in lines:
         print >> f, line
     f.close()
 
-def append_string(filename, line):
+def append_string(filename, line, mkdir = False):
     """Appends 'line' to file
     """
-    f = file(filename, 'a+')
+    f = mkdir_file(filename, 'a+', mkdir)
     print >> f, line
     f.close()
 
-def insert_string(filename, line):
+def insert_string(filename, line, mkdir = False):
     """Inserts 'line' at the beginning of the file
     """
-    f = file(filename, 'r+')
+    f = mkdir_file(filename, 'r+', mkdir)
     lines = f.readlines()
     f.seek(0); f.truncate()
     print >> f, line
     f.writelines(lines)
     f.close()
 
-def create_empty_file(name):
+def create_empty_file(name, mkdir = False):
     """Creates an empty file
     """
-    file(name, 'w+').close()
+    mkdir_file(name, 'w+', mkdir).close()
+
+def remove_leading_dir(leading, path):
+    """Remove leading directories from a pathname
+    """
+    if not path.startswith(leading):
+        raise Exception('"%s" does not begin with "%s"' % (path, leading))
+    path = path[len(leading):]
+    if len(path) > 0 and path[0] in [os.path.sep, os.path.altsep]:
+        path = path[1:]
+    return path
+
+def rmdir_while_empty(path, stop):
+    """Delete dirs until we reach a directory that isn't empty, or
+    until we reach the path stop
+    """
+    while path.startswith(stop) and len(path) > len(stop):
+        parent = os.path.dirname(path)
+        try:
+            os.rmdir(path)
+        except OSError:
+            return # directory not empty
+        path = parent
+
+def rm_file_and_dirs(path, stop):
+    """Delete the file, and keep deleting dirs until we reach a
+    directory that isn't empty, or until we reach the path stop
+    """
+    os.remove(path)
+    rmdir_while_empty(os.path.dirname(path), stop)
+
+def rm_if_exists(f):
+    """Delete file if it exists
+    """
+    if os.path.exists(f):
+        os.remove(f)
+
+def rename_dirs(from_path, to_path, stop):
+    """Rename file or directory, creating new directories at to_path
+    as necessary, and removing leftover empty directories at from_path
+    until we reach stop
+    """
+    if not os.path.isdir(os.path.dirname(to_path)):
+        os.makedirs(os.path.dirname(to_path))
+    os.rename(from_path, to_path)
+    rmdir_while_empty(os.path.dirname(from_path), stop)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help