[PATCH] Handle branch names with slashes
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 <kha@treskal.com>
---
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)