From d4356ac6143757131b58c95ca5c6a7b386cc6087 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Tue, 23 Oct 2007 22:20:53 +0100 Subject: [PATCH] Allow StGIT commands to run correctly in subdirectories Organization: Straylight/Edgeware From: Catalin Marinas This is mainly achieved by checking the file arguments with git-ls-files. The patch solves two main issues - the refresh/diff/st can run properly in subdirectories and commands like diff and status may no longer get nonexistent files as arguments without complaining. If one wants to check the status/diff or refresh the files in a subdirectory, the command arguments should be "." and this is expanded by git-ls-files to the subdirectory content (recursively). The patch removes some asserts as they are no longer needed since checks are performed by git-ls-files. Signed-off-by: Catalin Marinas --- stgit/commands/diff.py | 5 ++++- stgit/commands/patches.py | 6 ++++-- stgit/commands/refresh.py | 14 ++++++++------ stgit/commands/status.py | 11 +++++------ stgit/git.py | 30 +++++++++++++++++++++++++----- t/t0002-status.sh | 1 + t/t2300-refresh-subdir.sh | 21 +++++++++++++++++++++ 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/stgit/commands/diff.py b/stgit/commands/diff.py index 42e8367..1425518 100644 --- a/stgit/commands/diff.py +++ b/stgit/commands/diff.py @@ -27,7 +27,7 @@ from stgit import stack, git help = 'show the tree diff' -usage = """%prog [options] [] +usage = """%prog [options] [] Show the diff (default) or diffstat between the current working copy or a tree-ish object and another tree-ish object. File names can also @@ -56,6 +56,9 @@ options = [make_option('-r', '--range', def func(parser, options, args): """Show the tree diff """ + args = git.ls_files(args) + directory.cd_to_topdir() + if options.revs: rev_list = options.revs.split('..') rev_list_len = len(rev_list) diff --git a/stgit/commands/patches.py b/stgit/commands/patches.py index 0b618fe..140699d 100644 --- a/stgit/commands/patches.py +++ b/stgit/commands/patches.py @@ -26,7 +26,7 @@ from stgit import stack, git help = 'show the applied patches modifying a file' -usage = """%prog [options] [] +usage = """%prog [options] [] Show the applied patches modifying the given files. Without arguments, it shows the patches affected by the local tree modifications. The @@ -53,8 +53,10 @@ def func(parser, options, args): """ if not args: files = [path for (stat,path) in git.tree_status(verbose = True)] + # git.tree_status returns absolute paths else: - files = args + files = git.ls_files(args) + directory.cd_to_topdir() if not files: raise CmdException, 'No files specified or no local changes' diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py index 7cde392..6e8ed0c 100644 --- a/stgit/commands/refresh.py +++ b/stgit/commands/refresh.py @@ -27,7 +27,7 @@ from stgit.config import config help = 'generate a new commit for the current patch' -usage = """%prog [options] [] +usage = """%prog [options] [] Include the latest tree changes in the current patch. This command generates a new GIT commit object with the patch details, the previous @@ -37,7 +37,7 @@ options. The '--force' option is useful when a commit object was created with a different tool but the changes need to be included in the current patch.""" -directory = DirectoryGotoToplevel() +directory = DirectoryHasRepository() options = [make_option('-f', '--force', help = 'force the refresh even if HEAD and '\ 'top differ', @@ -55,8 +55,12 @@ options = [make_option('-f', '--force', ] def func(parser, options, args): - autoresolved = config.get('stgit.autoresolved') + """Generate a new commit for the current or given patch. + """ + args = git.ls_files(args) + directory.cd_to_topdir() + autoresolved = config.get('stgit.autoresolved') if autoresolved != 'yes': check_conflicts() @@ -81,9 +85,7 @@ def func(parser, options, args): out.done() return - files = [path for (stat,path) in git.tree_status(verbose = True)] - if args: - files = [f for f in files if f in args] + files = [path for (stat, path) in git.tree_status(files = args, verbose = True)] if files or not crt_series.head_top_equal(): if options.patch: diff --git a/stgit/commands/status.py b/stgit/commands/status.py index a688f7e..5763d09 100644 --- a/stgit/commands/status.py +++ b/stgit/commands/status.py @@ -25,7 +25,7 @@ from stgit import stack, git help = 'show the tree status' -usage = """%prog [options] [] +usage = """%prog [options] [] Show the status of the whole working copy or the given files. The command also shows the files in the current directory which are not @@ -72,7 +72,7 @@ def status(files = None, modified = False, new = False, deleted = False, """Show the tree status """ cache_files = git.tree_status(files, - unknown = (files == None), + unknown = (not files), noexclude = noexclude, diff_flags = diff_flags) filtered = (modified or new or deleted or conflict or unknown) @@ -94,7 +94,6 @@ def status(files = None, modified = False, new = False, deleted = False, output = [] for st, fn in cache_files: - assert files == None or fn in files if filtered: output.append(fn) else: @@ -105,6 +104,9 @@ def status(files = None, modified = False, new = False, deleted = False, def func(parser, options, args): """Show the tree status """ + args = git.ls_files(args) + directory.cd_to_topdir() + if options.reset: if args: for f in args: @@ -119,9 +121,6 @@ def func(parser, options, args): else: diff_flags = [] - # No args means all files - if not args: - args = None status(args, options.modified, options.new, options.deleted, options.conflict, options.unknown, options.noexclude, diff_flags = diff_flags) diff --git a/stgit/git.py b/stgit/git.py index 78aae05..7e1df1e 100644 --- a/stgit/git.py +++ b/stgit/git.py @@ -166,16 +166,37 @@ def exclude_files(): files.append(user_exclude) return files +def ls_files(files, tree = None, full_name = True): + """Return the files known to GIT or raise an error otherwise. It also + converts the file to the full path relative the the .git directory. + """ + if not files: + return [] + + args = [] + if tree: + args.append('--with-tree=%s' % tree) + if full_name: + args.append('--full-name') + args.append('--') + args.extend(files) + try: + return GRun('git-ls-files', '--error-unmatch', *args).output_lines() + except GitRunException: + # just hide the details of the git-ls-files command we use + raise GitException, \ + 'Some of the given paths are either missing or not known to GIT' + def tree_status(files = None, tree_id = 'HEAD', unknown = False, noexclude = True, verbose = False, diff_flags = []): """Get the status of all changed files, or of a selected set of files. Returns a list of pairs - (status, filename). - If 'files' is None, it will check all files, and optionally all + If 'not files', it will check all files, and optionally all unknown files. If 'files' is a list, it will only check the files in the list. """ - assert files == None or not unknown + assert not files or not unknown if verbose: out.start('Checking for changes in the working directory') @@ -204,11 +225,11 @@ def tree_status(files = None, tree_id = 'HEAD', unknown = False, if not conflicts: conflicts = [] cache_files += [('C', filename) for filename in conflicts - if files == None or filename in files] + if not files or filename in files] # the rest args = diff_flags + [tree_id] - if files != None: + if files: args += ['--'] + files for line in GRun('git-diff-index', *args).output_lines(): fs = tuple(line.rstrip().split(' ',4)[-1].split('\t',1)) @@ -218,7 +239,6 @@ def tree_status(files = None, tree_id = 'HEAD', unknown = False, if verbose: out.done() - assert files == None or set(f for s,f in cache_files) <= set(files) return cache_files def local_changes(verbose = True): diff --git a/t/t0002-status.sh b/t/t0002-status.sh index d0c31b2..6389a23 100755 --- a/t/t0002-status.sh +++ b/t/t0002-status.sh @@ -127,6 +127,7 @@ test_expect_success 'Status of file' ' ' cat > expected.txt < output.txt && diff --git a/t/t2300-refresh-subdir.sh b/t/t2300-refresh-subdir.sh index bdd27c5..750e429 100755 --- a/t/t2300-refresh-subdir.sh +++ b/t/t2300-refresh-subdir.sh @@ -24,4 +24,25 @@ test_expect_success 'Refresh again' ' [ "$(stg status)" = "" ] ' +test_expect_success 'Refresh file in subdirectory' ' + echo foo3 >> foo.txt && + echo bar3 >> bar/bar.txt && + cd bar && + stg refresh bar.txt && + cd .. && + [ "$(stg status)" = "M foo.txt" ] +' + +test_expect_success 'Refresh whole subdirectory' ' + echo bar4 >> bar/bar.txt && + stg refresh bar && + [ "$(stg status)" = "M foo.txt" ] +' + +test_expect_success 'Refresh subdirectories recursively' ' + echo bar5 >> bar/bar.txt && + stg refresh . && + [ "$(stg status)" = "" ] +' + test_done -- [mdw]