[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)