{"thread":{"id":"1231","subject":"[PATCH] stgit: allow spaces in filenames (second try)","startedAt":"2005-07-14T06:27:38Z","lastAt":"2005-07-14T15:01:48Z","messageCount":4,"participants":["Bryan Larsen","Catalin Marinas"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"6113","messageId":"20050714062733.21779.33599.sendpatchset@bryan-larsens-ibook-g4.local","threadId":"1231","inReplyTo":null,"subject":"[PATCH] stgit: allow spaces in filenames (second try)","fromName":"Bryan Larsen","fromEmail":"bryanlarsen@yahoo.com","sentAt":"2005-07-14T06:27:38Z","receivedAt":"2005-07-14T06:27:38Z","isPatch":true,"sender":{"key":"bryan@larsen.st","avatar":"https://avatars.githubusercontent.com/u/32073?v=4"},"body":"The current version of stgit does not allow whitespace in filenames.  This patch fixes that.  It also speeds up operations on large filesets considerably.\n\nSigned-off-by: Bryan Larsen <bryan.larsen@gmail.com>\n---\n\n stgit/commands/export.py |    4 -\n stgit/git.py             |  203 ++++++++++++++++++++++++----------------------\n 2 files changed, 110 insertions(+), 97 deletions(-)\n\ndiff --git a/stgit/commands/export.py b/stgit/commands/export.py\n--- a/stgit/commands/export.py\n+++ b/stgit/commands/export.py\n@@ -144,12 +144,12 @@ def func(parser, options, args):\n                   'supported in the patch template'\n         f = open(pfile, 'w+')\n         f.write(descr)\n-        f.close()\n \n         # write the diff\n         git.diff(rev1 = git_id('%s/bottom' % p),\n                  rev2 = git_id('%s/top' % p),\n-                 output = pfile, append = True)\n+                 out_fd = f)\n+        f.close()\n         patch_no += 1\n \n     series.close()\ndiff --git a/stgit/git.py b/stgit/git.py\n--- a/stgit/git.py\n+++ b/stgit/git.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, glob\n+import sys, os, glob, popen2\n \n from stgit.utils import *\n \n@@ -35,7 +35,6 @@ else:\n \n head_link = os.path.join(base_dir, 'HEAD')\n \n-\n #\n # Classes\n #\n@@ -44,9 +43,10 @@ class Commit:\n     \"\"\"\n     def __init__(self, id_hash):\n         self.__id_hash = id_hash\n-        f = os.popen('git-cat-file commit %s' % id_hash, 'r')\n \n-        for line in f:\n+        lines = _output_lines('git-cat-file commit %s' % id_hash)\n+        for i in range(len(lines)):\n+            line = lines[i]\n             if line == '\\n':\n                 break\n             field = line.strip().split(' ', 1)\n@@ -58,10 +58,7 @@ class Commit:\n                 self.__author = field[1]\n             if field[0] == 'comitter':\n                 self.__committer = field[1]\n-        self.__log = f.read()\n-\n-        if f.close():\n-            raise GitException, 'Unknown commit id'\n+        self.__log = ''.join(lines[i:])\n \n     def get_id_hash(self):\n         return self.__id_hash\n@@ -94,13 +91,46 @@ def get_conflicts():\n     else:\n         return None\n \n-def __output(cmd):\n-    f = os.popen(cmd, 'r')\n-    string = f.readline().strip()\n-    if f.close():\n-        raise GitException, '%s failed' % cmd\n+def _output(cmd):\n+    p=popen2.Popen3(cmd)\n+    string = p.fromchild.read()\n+    if p.wait():\n+        raise GitException, '%s failed' % str(cmd)\n     return string\n \n+def _output_one_line(cmd):\n+    p=popen2.Popen3(cmd)\n+    string = p.fromchild.readline().strip()\n+    if p.wait():\n+        raise GitException, '%s failed' % str(cmd)\n+    return string\n+\n+def _output_lines(cmd):\n+    p=popen2.Popen3(cmd)\n+    lines = p.fromchild.readlines()\n+    if p.wait():\n+        raise GitException, '%s failed' % str(cmd)\n+    return lines\n+\n+def __run(cmd, args=None):\n+    \"\"\"__run: runs cmd using spawnvp.\n+\n+    Runs cmd using spawnvp.  The shell is avoided so it won't mess up\n+    our arguments.  If args is very large, the command is run multiple\n+    times; args is split xargs style: cmd is passed on each\n+    invocation.  Unlike xargs, returns immediately if any non-zero\n+    return code is received.  \n+    \"\"\"\n+    \n+    args_l=cmd.split()\n+    if args is None:\n+        args = []\n+    for i in range(0, len(args)+1, 100):\n+        r=os.spawnvp(os.P_WAIT, args_l[0], args_l + args[i:min(i+100, len(args))])\n+    if r:\n+        return r\n+    return 0\n+\n def __check_base_dir():\n     return os.path.isdir(base_dir)\n \n@@ -114,14 +144,14 @@ def __tree_status(files = [], tree_id = \n     # unknown files\n     if unknown:\n         exclude_file = os.path.join(base_dir, 'exclude')\n-        extra_exclude = ''\n+        extra_exclude = []\n         if os.path.exists(exclude_file):\n-            extra_exclude += ' --exclude-from=%s' % exclude_file\n-        fout = os.popen('git-ls-files --others'\n-                        ' --exclude=\"*.[ao]\" --exclude=\".*\"'\n-                        ' --exclude=TAGS --exclude=tags --exclude=\"*~\"'\n-                        ' --exclude=\"#*\"' + extra_exclude, 'r')\n-        cache_files += [('?', line.strip()) for line in fout]\n+            extra_exclude.append('--exclude-from=%s' % exclude_file)\n+        lines = _output_lines(['git-ls-files', '--others',\n+                        '--exclude=*.[ao]', '--exclude=.*'\n+                        '--exclude=TAGS', '--exclude=tags', '--exclude=*~',\n+                        '--exclude=#*'] + extra_exclude)\n+        cache_files += [('?', line.strip()) for line in lines]\n \n     # conflicted files\n     conflicts = get_conflicts()\n@@ -130,14 +160,10 @@ def __tree_status(files = [], tree_id = \n     cache_files += [('C', filename) for filename in conflicts]\n \n     # the rest\n-    files_str = reduce(lambda x, y: x + ' ' + y, files, '')\n-    fout = os.popen('git-diff-cache -r %s %s' % (tree_id, files_str), 'r')\n-    for line in fout:\n-        fs = tuple(line.split()[4:])\n+    for line in _output_lines(['git-diff-cache', '-r', tree_id] + files):\n+        fs = tuple(line.rstrip().split(' ',4)[-1].split('\\t',1))\n         if fs[1] not in conflicts:\n             cache_files.append(fs)\n-    if fout.close():\n-        raise GitException, 'git-diff-cache failed'\n \n     return cache_files\n \n@@ -185,10 +211,9 @@ def add(names):\n         else:\n             raise GitException, '%s is not a file or directory' % i\n \n-    for f in files:\n-        print 'Adding file %s' % f\n-        if os.system('git-update-cache --add -- %s' % f) != 0:\n-            raise GitException, 'Unable to add %s' % f\n+    if files:\n+        if __run('git-update-cache --add --', files):\n+            raise GitException, 'Unable to add file'\n \n def rm(files, force = False):\n     \"\"\"Remove a file from the repository\n@@ -198,26 +223,32 @@ def rm(files, force = False):\n     else:\n         git_opt = '--remove'\n \n-    for f in files:\n-        if force:\n-            print 'Removing file %s' % f\n-            if os.system('git-update-cache --force-remove -- %s' % f) != 0:\n-                raise GitException, 'Unable to remove %s' % f\n-        elif os.path.exists(f):\n-            raise GitException, '%s exists. Remove it first' %f\n-        else:\n-            print 'Removing file %s' % f\n-            if os.system('git-update-cache --remove -- %s' % f) != 0:\n-                raise GitException, 'Unable to remove %s' % f\n+    if not force:\n+        for f in files:\n+            if os.path.exists(f):\n+                raise GitException, '%s exists. Remove it first' %f\n+        if files:\n+            __run('git-update-cache --remove --', files)\n+    else:\n+        if files:\n+            __run('git-update-cache --force-remove --', files)\n \n def update_cache(files):\n     \"\"\"Update the cache information for the given files\n     \"\"\"\n+    files_here = []\n+    files_gone = []\n+\n     for f in files:\n         if os.path.exists(f):\n-            os.system('git-update-cache -- %s' % f)\n+            files_here.append(f)\n         else:\n-            os.system('git-update-cache --remove -- %s' % f)\n+            files_gone.append(f)\n+\n+    if files_here:\n+        __run('git-update-cache --', files_here)\n+    if files_gone:\n+        __run('git-update-cache --remove --', files_gone)\n \n def commit(message, files = [], parents = [], allowempty = False,\n            author_name = None, author_email = None, author_date = None,\n@@ -248,19 +279,29 @@ def commit(message, files = [], parents \n \n     # update the cache\n     if not first:\n+        add_files=[]\n+        rm_files=[]\n+        m_files=[]\n         for f in cache_files:\n             if f[0] == 'N':\n-                git_flag = '--add'\n+                add_files.append(f[1])\n             elif f[0] == 'D':\n-                git_flag = '--force-remove'\n+                rm_files.append(f[1])\n             else:\n-                git_flag = '--'\n+                m_files.append(f[1])\n \n-            if os.system('git-update-cache %s %s' % (git_flag, f[1])) != 0:\n-                raise GitException, 'Failed git-update-cache -- %s' % f[1]\n+    if add_files:\n+        if __run('git-update-cache --add --', add_files):\n+            raise GitException, 'Failed git-update-cache --add'\n+    if rm_files:\n+        if __run('git-update-cache --force-remove --', rm_files):\n+            raise GitException, 'Failed git-update-cache --rm'\n+    if m_files:\n+        if __run('git-update-cache --', m_files):\n+            raise GitException, 'Failed git-update-cache'\n \n     # write the index to repository\n-    tree_id = __output('git-write-tree')\n+    tree_id = _output_one_line('git-write-tree')\n \n     # the commit\n     cmd = ''\n@@ -282,7 +323,7 @@ def commit(message, files = [], parents \n \n     cmd += ' < .commitmsg'\n \n-    commit_id = __output(cmd)\n+    commit_id = _output_one_line(cmd)\n     __set_head(commit_id)\n     os.remove('.commitmsg')\n \n@@ -292,7 +333,7 @@ def merge(base, head1, head2):\n     \"\"\"Perform a 3-way merge between base, head1 and head2 into the\n     local tree\n     \"\"\"\n-    if os.system('git-read-tree -u -m %s %s %s' % (base, head1, head2)) != 0:\n+    if __run('git-read-tree -u -m', [base, head1, head2]) != 0:\n         raise GitException, 'git-read-tree failed (local changes maybe?)'\n \n     # this can fail if there are conflicts\n@@ -330,51 +371,27 @@ def status(files = [], modified = False,\n         else:\n             print '%s' % fs[1]\n \n-def diff(files = [], rev1 = 'HEAD', rev2 = None, output = None,\n-         append = False):\n+def diff(files = [], rev1 = 'HEAD', rev2 = None, out_fd = sys.stdout):\n     \"\"\"Show the diff between rev1 and rev2\n     \"\"\"\n-    files_str = reduce(lambda x, y: x + ' ' + y, files, '')\n-\n-    extra_args = ''\n-    if output:\n-        if append:\n-            extra_args += ' >> %s' % output\n-        else:\n-            extra_args += ' > %s' % output\n-\n     os.system('git-update-cache --refresh > /dev/null')\n \n     if rev2:\n-        if os.system('git-diff-tree -p %s %s %s %s'\n-                     % (rev1, rev2, files_str, extra_args)) != 0:\n-            raise GitException, 'git-diff-tree failed'\n+        out_fd.write(_output(['git-diff-tree', '-p', rev1, rev2]+files))\n     else:\n-        if os.system('git-diff-cache -p %s %s %s'\n-                     % (rev1, files_str, extra_args)) != 0:\n-            raise GitException, 'git-diff-cache failed'\n+        out_fd.write(_output(['git-diff-cache', '-p', rev1]+files))\n \n def diffstat(files = [], rev1 = 'HEAD', rev2 = None):\n     \"\"\"Return the diffstat between rev1 and rev2\n     \"\"\"\n-    files_str = reduce(lambda x, y: x + ' ' + y, files, '')\n \n     os.system('git-update-cache --refresh > /dev/null')\n-    ds_cmd = '| git-apply --stat'\n-\n-    if rev2:\n-        f = os.popen('git-diff-tree -p %s %s %s %s'\n-                     % (rev1, rev2, files_str, ds_cmd), 'r')\n-        str = f.read().rstrip()\n-        if f.close():\n-            raise GitException, 'git-diff-tree failed'\n-    else:\n-        f = os.popen('git-diff-cache -p %s %s %s'\n-                     % (rev1, files_str, ds_cmd), 'r')\n-        str = f.read().rstrip()\n-        if f.close():\n-            raise GitException, 'git-diff-cache failed'\n-\n+    p=popen2.Popen3('git-apply --stat')\n+    diff(files, rev1, rev2, p.tochild)\n+    p.tochild.close()\n+    str = p.fromchild.read().rstrip()\n+    if p.wait():\n+        raise GitException, 'git.diffstat failed'\n     return str\n \n def files(rev1, rev2):\n@@ -383,35 +400,31 @@ def files(rev1, rev2):\n     os.system('git-update-cache --refresh > /dev/null')\n \n     str = ''\n-    f = os.popen('git-diff-tree -r %s %s' % (rev1, rev2),\n-                 'r')\n-    for line in f:\n-        str += '%s %s\\n' % tuple(line.split()[4:])\n-    if f.close():\n-        raise GitException, 'git-diff-tree failed'\n+    for line in _output_lines('git-diff-tree -r %s %s' % (rev1, rev2)):\n+        str += '%s %s\\n' % tuple(line.rstrip().split(' ',4)[-1].split('\\t',1))\n \n     return str.rstrip()\n \n def checkout(files = [], force = False):\n     \"\"\"Check out the given or all files\n     \"\"\"\n-    git_flags = ''\n+    git_flags = 'git-checkout-cache -q -u'\n     if force:\n         git_flags += ' -f'\n     if len(files) == 0:\n         git_flags += ' -a'\n     else:\n-        git_flags += reduce(lambda x, y: x + ' ' + y, files, ' --')\n+        git_flags += ' --'\n \n-    if os.system('git-checkout-cache -q -u%s' % git_flags) != 0:\n-        raise GitException, 'Failed git-checkout-cache -q -u%s' % git_flags\n+    if __run(git_flags, files) != 0:\n+        raise GitException, 'Failed git-checkout-cache'\n \n def switch(tree_id):\n     \"\"\"Switch the tree to the given id\n     \"\"\"\n     to_delete = filter(lambda x: x[0] == 'N', __tree_status(tree_id = tree_id))\n \n-    if os.system('git-read-tree -m %s' % tree_id) != 0:\n+    if __run('git-read-tree -m', [tree_id]) != 0:\n         raise GitException, 'Failed git-read-tree -m %s' % tree_id\n \n     checkout(force = True)\n"},{"id":"6114","messageId":"20050714062744.21779.87923.sendpatchset@bryan-larsens-ibook-g4.local","threadId":"1231","inReplyTo":"20050714062733.21779.33599.sendpatchset@bryan-larsens-ibook-g4.local","subject":"[PATCH] stgit: export without --range does not export the last patch","fromName":"Bryan Larsen","fromEmail":"bryanlarsen@yahoo.com","sentAt":"2005-07-14T06:27:56Z","receivedAt":"2005-07-14T06:27:56Z","isPatch":true,"sender":{"key":"bryan@larsen.st","avatar":"https://avatars.githubusercontent.com/u/32073?v=4"},"body":"The top patch is never exported.\n\nSigned-off-by: Bryan Larsen <bryan.larsen@gmail.com>\n---\n\n stgit/commands/export.py |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/stgit/commands/export.py b/stgit/commands/export.py\n--- a/stgit/commands/export.py\n+++ b/stgit/commands/export.py\n@@ -90,7 +90,7 @@ def func(parser, options, args):\n             raise MainException, 'Incorrect patch range order'\n     else:\n         start_idx = 0\n-        stop_idx = -1\n+        stop_idx = len(applied)\n \n     patches = applied[start_idx:stop_idx]\n \n"},{"id":"6131","messageId":"tnxu0ixn0ux.fsf@arm.com","threadId":"1231","inReplyTo":"20050714062733.21779.33599.sendpatchset@bryan-larsens-ibook-g4.local","subject":"Re: [PATCH] stgit: allow spaces in filenames (second try)","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-07-14T10:22:30Z","receivedAt":"2005-07-14T10:22:30Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"Bryan Larsen <bryanlarsen@yahoo.com> wrote:\n> The current version of stgit does not allow whitespace in filenames.  This patch fixes that.  It also speeds up operations on large filesets considerably.\n>\n> Signed-off-by: Bryan Larsen <bryan.larsen@gmail.com>\n\nApplied. It will be visible tonight via the ftp mirror.\n\nOne note about patch description. I would prefer to have the\nconvention of the Linux kernel patches:\n\n---\nShort description line\n\nLonger\ndescription\n\nSigned-off-by: ...\n---\n\nA future export command with support for sendpatcheset will take the\nshort description line and use it as a subject. Also, it would be nice\nfor the longer description to be wrapped somewhere before column 80\n(~72 would be OK).\n\n-- \nCatalin\n"},{"id":"6132","messageId":"tnx3bqhtorn.fsf@arm.com","threadId":"1231","inReplyTo":"tnxu0ixn0ux.fsf@arm.com","subject":"Re: [PATCH] stgit: allow spaces in filenames (second try)","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2005-07-14T15:01:48Z","receivedAt":"2005-07-14T15:01:48Z","isPatch":true,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"Catalin Marinas <catalin.marinas@gmail.com> wrote:\n> One note about patch description. I would prefer to have the\n> convention of the Linux kernel patches:\n>\n> ---\n> Short description line\n\nProbably without this line in the e-mail body since it is already in\nthe subject line.\n\n-- \nCatalin\n"}]}