From 49a7771336ce09f6d42c7699ef32aecea0e83182 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 7 Dec 2009 20:07:55 -0500 Subject: Initial directory restructuring to clarify dependencies --- libbe/storage/vcs/bzr.py | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 libbe/storage/vcs/bzr.py (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py new file mode 100644 index 0000000..62a9b11 --- /dev/null +++ b/libbe/storage/vcs/bzr.py @@ -0,0 +1,117 @@ +# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. +# Ben Finney +# Gianluca Montecchi +# Marien Zwart +# W. Trevor King +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Bazaar (bzr) backend. +""" + +import os +import re +import sys +import unittest + +import libbe +import vcs +if libbe.TESTING == True: + import doctest + + +def new(): + return Bzr() + +class Bzr(vcs.VCS): + name = "bzr" + client = "bzr" + versioned = True + def _vcs_version(self): + status,output,error = self._u_invoke_client("--version") + return output + def _vcs_detect(self, path): + if self._u_search_parent_directories(path, ".bzr") != None : + return True + return False + def _vcs_root(self, path): + """Find the root of the deepest repository containing path.""" + status,output,error = self._u_invoke_client("root", path) + return output.rstrip('\n') + def _vcs_init(self, path): + self._u_invoke_client("init", cwd=path) + def _vcs_get_user_id(self): + status,output,error = self._u_invoke_client("whoami") + return output.rstrip('\n') + def _vcs_set_user_id(self, value): + self._u_invoke_client("whoami", value) + def _vcs_add(self, path): + self._u_invoke_client("add", path) + def _vcs_remove(self, path): + # --force to also remove unversioned files. + self._u_invoke_client("remove", "--force", path) + def _vcs_update(self, path): + pass + def _vcs_get_file_contents(self, path, revision=None, binary=False): + if revision == None: + return vcs.VCS._vcs_get_file_contents(self, path, revision, binary=binary) + else: + status,output,error = \ + self._u_invoke_client("cat","-r",revision,path) + return output + def _vcs_duplicate_repo(self, directory, revision=None): + if revision == None: + vcs.VCS._vcs_duplicate_repo(self, directory, revision) + else: + self._u_invoke_client("branch", "--revision", revision, + ".", directory) + def _vcs_commit(self, commitfile, allow_empty=False): + args = ["commit", "--file", commitfile] + if allow_empty == True: + args.append("--unchanged") + status,output,error = self._u_invoke_client(*args) + else: + kwargs = {"expect":(0,3)} + status,output,error = self._u_invoke_client(*args, **kwargs) + if status != 0: + strings = ["ERROR: no changes to commit.", # bzr 1.3.1 + "ERROR: No changes to commit."] # bzr 1.15.1 + if self._u_any_in_string(strings, error) == True: + raise vcs.EmptyCommit() + else: + raise vcs.CommandError(args, status, stderr=error) + revision = None + revline = re.compile("Committed revision (.*)[.]") + match = revline.search(error) + assert match != None, output+error + assert len(match.groups()) == 1 + revision = match.groups()[0] + return revision + def _vcs_revision_id(self, index): + status,output,error = self._u_invoke_client("revno") + current_revision = int(output) + if index >= current_revision or index < -current_revision: + return None + if index >= 0: + return str(index+1) # bzr commit 0 is the empty tree. + return str(current_revision+index+1) + + +if libbe.TESTING == True: + vcs.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__]) + + unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()]) -- cgit From ca52b5cca130fb3bd810276d9de1f198df3cf5b7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 13 Dec 2009 07:45:43 -0500 Subject: .bzr transition. --- libbe/storage/vcs/bzr.py | 83 ++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 35 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 62a9b11..c847019 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -23,12 +23,14 @@ Bazaar (bzr) backend. """ import os +import os.path import re +import shutil import sys import unittest import libbe -import vcs +import base if libbe.TESTING == True: import doctest @@ -36,72 +38,83 @@ if libbe.TESTING == True: def new(): return Bzr() -class Bzr(vcs.VCS): - name = "bzr" - client = "bzr" - versioned = True +class Bzr(base.VCS): + name = 'bzr' + client = 'bzr' + + def __init__(self, *args, **kwargs): + base.VCS.__init__(self, *args, **kwargs) + self.versioned = True + def _vcs_version(self): - status,output,error = self._u_invoke_client("--version") + status,output,error = self._u_invoke_client('--version') return output + + def _vcs_get_user_id(self): + status,output,error = self._u_invoke_client('whoami') + return output.rstrip('\n') + def _vcs_detect(self, path): - if self._u_search_parent_directories(path, ".bzr") != None : + if self._u_search_parent_directories(path, '.bzr') != None : return True return False + def _vcs_root(self, path): """Find the root of the deepest repository containing path.""" - status,output,error = self._u_invoke_client("root", path) + status,output,error = self._u_invoke_client('root', path) return output.rstrip('\n') + def _vcs_init(self, path): - self._u_invoke_client("init", cwd=path) - def _vcs_get_user_id(self): - status,output,error = self._u_invoke_client("whoami") - return output.rstrip('\n') - def _vcs_set_user_id(self, value): - self._u_invoke_client("whoami", value) + self._u_invoke_client('init', cwd=path) + + def _vcs_destroy(self): + vcs_dir = os.path.join(self.repo, '.bzr') + if os.path.exists(vcs_dir): + shutil.rmtree(vcs_dir) + def _vcs_add(self, path): - self._u_invoke_client("add", path) + self._u_invoke_client('add', path) + def _vcs_remove(self, path): # --force to also remove unversioned files. - self._u_invoke_client("remove", "--force", path) + self._u_invoke_client('remove', '--force', path) + def _vcs_update(self, path): pass - def _vcs_get_file_contents(self, path, revision=None, binary=False): + + def _vcs_get_file_contents(self, path, revision=None): if revision == None: - return vcs.VCS._vcs_get_file_contents(self, path, revision, binary=binary) + return base.VCS._vcs_get_file_contents(self, path, revision) else: status,output,error = \ - self._u_invoke_client("cat","-r",revision,path) + self._u_invoke_client('cat', '-r', revision,path) return output - def _vcs_duplicate_repo(self, directory, revision=None): - if revision == None: - vcs.VCS._vcs_duplicate_repo(self, directory, revision) - else: - self._u_invoke_client("branch", "--revision", revision, - ".", directory) + def _vcs_commit(self, commitfile, allow_empty=False): - args = ["commit", "--file", commitfile] + args = ['commit', '--file', commitfile] if allow_empty == True: - args.append("--unchanged") + args.append('--unchanged') status,output,error = self._u_invoke_client(*args) else: - kwargs = {"expect":(0,3)} + kwargs = {'expect':(0,3)} status,output,error = self._u_invoke_client(*args, **kwargs) if status != 0: - strings = ["ERROR: no changes to commit.", # bzr 1.3.1 - "ERROR: No changes to commit."] # bzr 1.15.1 + strings = ['ERROR: no changes to commit.', # bzr 1.3.1 + 'ERROR: No changes to commit.'] # bzr 1.15.1 if self._u_any_in_string(strings, error) == True: - raise vcs.EmptyCommit() + raise base.EmptyCommit() else: - raise vcs.CommandError(args, status, stderr=error) + raise base.CommandError(args, status, stderr=error) revision = None - revline = re.compile("Committed revision (.*)[.]") + revline = re.compile('Committed revision (.*)[.]') match = revline.search(error) assert match != None, output+error assert len(match.groups()) == 1 revision = match.groups()[0] return revision + def _vcs_revision_id(self, index): - status,output,error = self._u_invoke_client("revno") + status,output,error = self._u_invoke_client('revno') current_revision = int(output) if index >= current_revision or index < -current_revision: return None @@ -111,7 +124,7 @@ class Bzr(vcs.VCS): if libbe.TESTING == True: - vcs.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__]) + base.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__]) unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()]) -- cgit From d21c50ece316536b5972725eced19b40d6e2589d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 13 Dec 2009 08:31:33 -0500 Subject: Fix Git._vcs_revision_id() offset bug. --- libbe/storage/vcs/bzr.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index c847019..7d84415 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -26,13 +26,14 @@ import os import os.path import re import shutil -import sys -import unittest import libbe import base + if libbe.TESTING == True: import doctest + import sys + import unittest def new(): -- cgit From 86b5fba698855cb4709d6f009e84b4002361f0db Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 13 Dec 2009 20:24:21 -0500 Subject: Adjust Bzr._vcs_revision_id for 1-indexed revision ids. --- libbe/storage/vcs/bzr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 7d84415..04cc6c1 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -120,7 +120,7 @@ class Bzr(base.VCS): if index >= current_revision or index < -current_revision: return None if index >= 0: - return str(index+1) # bzr commit 0 is the empty tree. + return str(index) # bzr commit 0 is the empty tree. return str(current_revision+index+1) -- cgit From 89b7a1411e4658e831f5d635534b24355dbb941d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 15 Dec 2009 06:44:20 -0500 Subject: Fixed libbe.command.diff + ugly BugDir.duplicate_bugdir implementation duplicate_bugdir() works, but for the vcs backends, it could require shelling out for _every_ file read. This could, and probably will, be horribly slow. Still it works ;). I'm not sure what a better implementation would be. The old implementation checked out the entire earlier state into a temporary directory pros: single shell out, simple upgrade implementation cons: wouldn't work well for HTTP backens I think a good solution would run along the lines of the currently commented out code in duplicate_bugdir(), where a VersionedStorage.changed_since(revision) call would give you a list of changed files. diff could work off of that directly, without the need to generate a whole duplicate bugdir. I'm stuck on how to handle upgrades though... Also removed trailing whitespace from all python files. --- libbe/storage/vcs/bzr.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 04cc6c1..6f3e840 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -49,7 +49,7 @@ class Bzr(base.VCS): def _vcs_version(self): status,output,error = self._u_invoke_client('--version') - return output + return output def _vcs_get_user_id(self): status,output,error = self._u_invoke_client('whoami') @@ -88,7 +88,7 @@ class Bzr(base.VCS): return base.VCS._vcs_get_file_contents(self, path, revision) else: status,output,error = \ - self._u_invoke_client('cat', '-r', revision,path) + self._u_invoke_client('cat', '-r', revision, path) return output def _vcs_commit(self, commitfile, allow_empty=False): @@ -123,7 +123,7 @@ class Bzr(base.VCS): return str(index) # bzr commit 0 is the empty tree. return str(current_revision+index+1) - + if libbe.TESTING == True: base.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__]) -- cgit From f96762deddc0cb6b1380abdcbbe7347ae23f18a1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 28 Dec 2009 10:28:58 -0500 Subject: Bzr storage now based off bzrlib module, not 'bzr' executible. This should make repeated calls to Bzr storage instances _much_ faster, since we avoid repeatedly loading and tearing down a python subprocess. --- libbe/storage/vcs/bzr.py | 114 +++++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 38 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 6f3e840..4e3f330 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -22,10 +22,20 @@ Bazaar (bzr) backend. """ +try: + import bzrlib + import bzrlib.branch + import bzrlib.builtins + import bzrlib.config + import bzrlib.errors + import bzrlib.option +except ImportError: + bzrlib = None import os import os.path import re import shutil +import StringIO import libbe import base @@ -41,19 +51,24 @@ def new(): class Bzr(base.VCS): name = 'bzr' - client = 'bzr' + client = None # bzrlib def __init__(self, *args, **kwargs): base.VCS.__init__(self, *args, **kwargs) self.versioned = True def _vcs_version(self): - status,output,error = self._u_invoke_client('--version') - return output + if bzrlib == None: + return None + return bzrlib.__version__ def _vcs_get_user_id(self): - status,output,error = self._u_invoke_client('whoami') - return output.rstrip('\n') + # excerpted from bzrlib.builtins.cmd_whoami.run() + try: + c = bzrlib.branch.Branch.open_containing(self.repo)[0].get_config() + except errors.NotBranchError: + c = bzrlib.config.GlobalConfig() + return c.username() def _vcs_detect(self, path): if self._u_search_parent_directories(path, '.bzr') != None : @@ -62,11 +77,15 @@ class Bzr(base.VCS): def _vcs_root(self, path): """Find the root of the deepest repository containing path.""" - status,output,error = self._u_invoke_client('root', path) - return output.rstrip('\n') + cmd = bzrlib.builtins.cmd_root() + cmd.outf = StringIO.StringIO() + cmd.run(filename=path) + return cmd.outf.getvalue().rstrip('\n') def _vcs_init(self, path): - self._u_invoke_client('init', cwd=path) + cmd = bzrlib.builtins.cmd_init() + cmd.outf = StringIO.StringIO() + cmd.run(location=path) def _vcs_destroy(self): vcs_dir = os.path.join(self.repo, '.bzr') @@ -74,50 +93,69 @@ class Bzr(base.VCS): shutil.rmtree(vcs_dir) def _vcs_add(self, path): - self._u_invoke_client('add', path) + path = os.path.join(self.repo, path) + cmd = bzrlib.builtins.cmd_add() + cmd.outf = StringIO.StringIO() + cmd.run(file_list=[path], file_ids_from=self.repo) def _vcs_remove(self, path): # --force to also remove unversioned files. - self._u_invoke_client('remove', '--force', path) + path = os.path.join(self.repo, path) + cmd = bzrlib.builtins.cmd_remove() + cmd.outf = StringIO.StringIO() + cmd.run(file_list=[path], file_deletion_strategy='force') def _vcs_update(self, path): pass + def _parse_revision_string(self, revision=None): + if revision == None: + return revision + rev_opt = bzrlib.option.Option.OPTIONS['revision'] + try: + rev_spec = rev_opt.type(revision) + except bzrlib.errors.NoSuchRevisionSpec: + raise base.InvalidRevision(revision) + return rev_spec + def _vcs_get_file_contents(self, path, revision=None): if revision == None: return base.VCS._vcs_get_file_contents(self, path, revision) - else: - status,output,error = \ - self._u_invoke_client('cat', '-r', revision, path) - return output + path = os.path.join(self.repo, path) + revision = self._parse_revision_string(revision) + cmd = bzrlib.builtins.cmd_cat() + cmd.outf = StringIO.StringIO() + try: + cmd.run(filename=path, revision=revision) + except bzrlib.errors.BzrCommandError, e: + if 'not present in revision' in str(e): + raise base.InvalidID(path) + raise + return cmd.outf.getvalue() def _vcs_commit(self, commitfile, allow_empty=False): - args = ['commit', '--file', commitfile] - if allow_empty == True: - args.append('--unchanged') - status,output,error = self._u_invoke_client(*args) - else: - kwargs = {'expect':(0,3)} - status,output,error = self._u_invoke_client(*args, **kwargs) - if status != 0: - strings = ['ERROR: no changes to commit.', # bzr 1.3.1 - 'ERROR: No changes to commit.'] # bzr 1.15.1 - if self._u_any_in_string(strings, error) == True: - raise base.EmptyCommit() - else: - raise base.CommandError(args, status, stderr=error) - revision = None - revline = re.compile('Committed revision (.*)[.]') - match = revline.search(error) - assert match != None, output+error - assert len(match.groups()) == 1 - revision = match.groups()[0] - return revision + cmd = bzrlib.builtins.cmd_commit() + cmd.outf = StringIO.StringIO() + cwd = os.getcwd() + os.chdir(self.repo) + try: + cmd.run(file=commitfile, unchanged=allow_empty) + except bzrlib.errors.BzrCommandError, e: + strings = ['no changes to commit.', # bzr 1.3.1 + 'No changes to commit.'] # bzr 1.15.1 + if self._u_any_in_string(strings, str(e)) == True: + raise base.EmptyCommit() + raise + finally: + os.chdir(cwd) + return self._vcs_revision_id(-1) def _vcs_revision_id(self, index): - status,output,error = self._u_invoke_client('revno') - current_revision = int(output) - if index >= current_revision or index < -current_revision: + cmd = bzrlib.builtins.cmd_revno() + cmd.outf = StringIO.StringIO() + cmd.run(location=self.repo) + current_revision = int(cmd.outf.getvalue()) + if index > current_revision or index < -current_revision: return None if index >= 0: return str(index) # bzr commit 0 is the empty tree. -- cgit From c90044dff5feaf5f43fee9e8559fecec2ec60091 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 28 Dec 2009 12:30:19 -0500 Subject: Fixed VCS.children() and Bzr.children() for non-None revisions. Now they both pass VersionedStorage_commit_TestCase.test_commit_revision_ids() The .children() implementation for previous revisions lacks the working directory's id<->path cache, so it's fairly slow... --- libbe/storage/vcs/bzr.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 4e3f330..d6e7799 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -129,10 +129,37 @@ class Bzr(base.VCS): cmd.run(filename=path, revision=revision) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): - raise base.InvalidID(path) + raise base.InvalidID(path, revision) raise return cmd.outf.getvalue() + def _vcs_path(self, id, revision): + return self._u_find_id(id, revision) + + def _vcs_isdir(self, path, revision): + try: + self._vcs_listdir(path, revision) + except AttributeError, e: + if 'children' in str(e): + return False + raise + return True + + def _vcs_listdir(self, path, revision): + path = os.path.join(self.repo, path) + revision = self._parse_revision_string(revision) + cmd = bzrlib.builtins.cmd_ls() + cmd.outf = StringIO.StringIO() + try: + cmd.run(revision=revision, path=path) + except bzrlib.errors.BzrCommandError, e: + if 'not present in revision' in str(e): + raise base.InvalidID(path, revision) + raise + children = cmd.outf.getvalue().rstrip('\n').splitlines() + children = [self._u_rel_path(c, path) for c in children] + return children + def _vcs_commit(self, commitfile, allow_empty=False): cmd = bzrlib.builtins.cmd_commit() cmd.outf = StringIO.StringIO() -- cgit From e0e7328742b92cb5e08aeec348fce966375d7d52 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 28 Dec 2009 13:22:27 -0500 Subject: Updated Git backend to support .children(revision). + some minor fixes to vcs/base.py and vcs/bzr.py Also removed .be/id-cache, which should never have been versioned in the first place. --- libbe/storage/vcs/bzr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index d6e7799..397267a 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -129,7 +129,7 @@ class Bzr(base.VCS): cmd.run(filename=path, revision=revision) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): - raise base.InvalidID(path, revision) + raise base.InvalidPath(path, root=self.repo, revision=revision) raise return cmd.outf.getvalue() @@ -154,7 +154,7 @@ class Bzr(base.VCS): cmd.run(revision=revision, path=path) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): - raise base.InvalidID(path, revision) + raise base.InvalidPath(path, root=self.repo, revision=revision) raise children = cmd.outf.getvalue().rstrip('\n').splitlines() children = [self._u_rel_path(c, path) for c in children] -- cgit From 0aa80631bd2dc0a5f28f1dd7db2cbda7d14e67fe Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 29 Dec 2009 05:52:27 -0500 Subject: Hg storage now based off mercurial module, not 'hg' executible. This should make repeated calls to Hg storage instances _much_ faster, since we avoid repeatedly loading and tearing down a python subprocess. For example, the testsuite runs ~6x faster on my box. Here's a run with the old Hg implementation: $ python test.py libbe.storage.vcs.hg ... ================================= ERROR: test_get_previous_children --------------------------------- Traceback (most recent call last): ... NotImplementedError --------------------------------- Ran 49 tests in 133.285s FAILED (errors=1) A run with the new implementation gives the same results, except for: Ran 49 tests in 22.328s --- libbe/storage/vcs/bzr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 397267a..7335861 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -51,7 +51,7 @@ def new(): class Bzr(base.VCS): name = 'bzr' - client = None # bzrlib + client = None # bzrlib module def __init__(self, *args, **kwargs): base.VCS.__init__(self, *args, **kwargs) -- cgit From 4d4283ecd654f1efb058cd7f7dba6be88b70ee92 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 08:11:08 -0500 Subject: Updated copyright information --- libbe/storage/vcs/bzr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 7335861..4d72fd0 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -1,4 +1,4 @@ -# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. +# Copyright (C) 2005-2010 Aaron Bentley and Panometrics, Inc. # Ben Finney # Gianluca Montecchi # Marien Zwart -- cgit From 9fd1decbc4631a8d4d3fcbfde11358ec215be162 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 18 Jan 2010 08:45:16 -0500 Subject: Add .changed() support to Bzr --- libbe/storage/vcs/bzr.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 4d72fd0..285ecf1 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -36,13 +36,13 @@ import os.path import re import shutil import StringIO +import sys import libbe import base if libbe.TESTING == True: import doctest - import sys import unittest @@ -188,6 +188,88 @@ class Bzr(base.VCS): return str(index) # bzr commit 0 is the empty tree. return str(current_revision+index+1) + def _diff(self, revision): + revision = self._parse_revision_string(revision) + cmd = bzrlib.builtins.cmd_diff() + cmd.outf = StringIO.StringIO() + # for some reason, cmd_diff uses sys.stdout not self.outf for output. + stdout = sys.stdout + sys.stdout = cmd.outf + try: + status = cmd.run(revision=revision, file_list=[self.repo]) + finally: + sys.stdout = stdout + assert status in [0,1], "Invalid status %d" % status + return cmd.outf.getvalue() + + def _parse_diff(self, diff_text): + """ + Example diff text: + + === modified file 'dir/changed' + --- dir/changed 2010-01-16 01:54:53 +0000 + +++ dir/changed 2010-01-16 01:54:54 +0000 + @@ -1,3 +1,3 @@ + hi + -there + +everyone and + joe + + === removed file 'dir/deleted' + --- dir/deleted 2010-01-16 01:54:53 +0000 + +++ dir/deleted 1970-01-01 00:00:00 +0000 + @@ -1,3 +0,0 @@ + -in + -the + -beginning + + === removed file 'dir/moved' + --- dir/moved 2010-01-16 01:54:53 +0000 + +++ dir/moved 1970-01-01 00:00:00 +0000 + @@ -1,4 +0,0 @@ + -the + -ants + -go + -marching + + === added file 'dir/moved2' + --- dir/moved2 1970-01-01 00:00:00 +0000 + +++ dir/moved2 2010-01-16 01:54:34 +0000 + @@ -0,0 +1,4 @@ + +the + +ants + +go + +marching + + === added file 'dir/new' + --- dir/new 1970-01-01 00:00:00 +0000 + +++ dir/new 2010-01-16 01:54:54 +0000 + @@ -0,0 +1,2 @@ + +hello + +world + + """ + new = [] + modified = [] + removed = [] + lines = diff_text.splitlines() + for i,line in enumerate(lines): + if not line.startswith('=== '): + continue + fields = line.split() + action = fields[1] + file = fields[-1].strip("'") + if action == 'added': + new.append(file) + elif action == 'modified': + modified.append(file) + elif action == 'removed': + removed.append(file) + return (new,modified,removed) + + def _vcs_changed(self, revision): + return self._parse_diff(self._diff(revision)) + if libbe.TESTING == True: base.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__]) -- cgit From 7ae29f930fe73adada5174a2ce74266411809ac7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 18 Jan 2010 12:25:17 -0500 Subject: Added VCS._u_find_id_from_manifest for faster id->path calculation --- libbe/storage/vcs/bzr.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 285ecf1..e1cd2e5 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -134,7 +134,9 @@ class Bzr(base.VCS): return cmd.outf.getvalue() def _vcs_path(self, id, revision): - return self._u_find_id(id, revision) + manifest = self._vcs_listdir( + self.repo, revision=revision, recursive=True) + return self._u_find_id_from_manifest(id, manifest, revision=revision) def _vcs_isdir(self, path, revision): try: @@ -145,13 +147,13 @@ class Bzr(base.VCS): raise return True - def _vcs_listdir(self, path, revision): + def _vcs_listdir(self, path, revision, recursive=False): path = os.path.join(self.repo, path) revision = self._parse_revision_string(revision) cmd = bzrlib.builtins.cmd_ls() cmd.outf = StringIO.StringIO() try: - cmd.run(revision=revision, path=path) + cmd.run(revision=revision, path=path, recursive=recursive) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): raise base.InvalidPath(path, root=self.repo, revision=revision) @@ -252,8 +254,7 @@ class Bzr(base.VCS): new = [] modified = [] removed = [] - lines = diff_text.splitlines() - for i,line in enumerate(lines): + for line in diff_text.splitlines(): if not line.startswith('=== '): continue fields = line.split() -- cgit From e9c0a069dc1819fc3225501f362c3e9c130cb72b Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 22 Jan 2010 14:14:03 -0500 Subject: Add Bzr._vcs_exists() anyway :p --- libbe/storage/vcs/bzr.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index e1cd2e5..1db50f8 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -98,6 +98,13 @@ class Bzr(base.VCS): cmd.outf = StringIO.StringIO() cmd.run(file_list=[path], file_ids_from=self.repo) + def _vcs_exists(self, path, revision=None): + manifest = self._vcs_listdir( + self.repo, revision=revision, recursive=True) + if path in manifest: + return True + return False + def _vcs_remove(self, path): # --force to also remove unversioned files. path = os.path.join(self.repo, path) @@ -131,7 +138,7 @@ class Bzr(base.VCS): if 'not present in revision' in str(e): raise base.InvalidPath(path, root=self.repo, revision=revision) raise - return cmd.outf.getvalue() + return cmd.outf.getvalue() def _vcs_path(self, id, revision): manifest = self._vcs_listdir( -- cgit From 76d983ec670ec7f09dace232e8553a80b2a08878 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 28 Jan 2010 11:47:59 -0500 Subject: Fix bzrlib.builtins.cmd_ls() recursion argument for pre 2.0 bzrlibs. $ python test.py libbe.storage.vcs.bzr ... ====================================================================== ERROR: Children list should be revision dependent. ---------------------------------------------------------------------- Traceback (most recent call last): File ".../libbe/storage/base.py", line 997, in test_ get_previous_children ret = sorted(self.s.children('parent', revision=revs[i])) File ".../libbe/storage/base.py", line 314, in child ren return self._children(*args, **kwargs) File ".../libbe/storage/vcs/base.py", line 811, in _ children path = self.path(id, revision, relpath=False) File ".../libbe/storage/vcs/base.py", line 716, in p ath path = self._vcs_path(id, revision) File ".../libbe/storage/vcs/bzr.py", line 145, in _v cs_path self.repo, revision=revision, recursive=True) File ".../libbe/storage/vcs/bzr.py", line 163, in _v cs_listdir cmd.run(revision=revision, path=path, recursive=recursive) File "/usr/lib/python2.5/site-packages/bzrlib/commands.py", line 800, in ignor e_pipe result = func(*args, **kwargs) TypeError: run() got an unexpected keyword argument 'recursive' ... The change is due to (in bzr.dev): revno: 4206.2.1 revision-id: ian.clatworthy@canonical.com-20090326133831-orvicmmc6w29mpfp parent: pqm@pqm.ubuntu.com-20090326063330-evutyvml3067dpsz committer: Ian Clatworthy branch nick: bzr.ls-recursive-off timestamp: Thu 2009-03-26 23:38:31 +1000 message: ls should be non-recursive by default Which occured between bzr-1.9rc1 and 2.0rc1.: bzr.dev$ bzr tags 2.0rc1 4634.9.1 ... bzr-1.9rc1 3815.3.1 bzr-2.0.1 4634.73.2 ... --- libbe/storage/vcs/bzr.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 1db50f8..b617d68 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -62,6 +62,46 @@ class Bzr(base.VCS): return None return bzrlib.__version__ + def version_cmp(self, *args): + """ + Compare the installed Bazaar version V_i with another version + V_o (given in *args). Returns + 1 if V_i > V_o, + 0 if V_i == V_o, and + -1 if V_i < V_o + >>> b = Bzr(repo='.') + >>> b._vcs_version = lambda : "2.3.1 (release)" + >>> b.version_cmp(2,3,1) + 0 + >>> b.version_cmp(2,3,2) + -1 + >>> b.version_cmp(2,3,0) + 1 + >>> b.version_cmp(3) + -1 + >>> b._vcs_version = lambda : "2.0.0pre2" + >>> b._parsed_version = None + >>> b.version_cmp(3) + Traceback (most recent call last): + ... + NotImplementedError: Cannot parse "2.0.0pre2" portion of Bazaar version "2.0.0pre2" + invalid literal for int() with base 10: '0pre2' + """ + if not hasattr(self, '_parsed_version') \ + or self._parsed_version == None: + num_part = self._vcs_version().split(' ')[0] + try: + self._parsed_version = [int(i) for i in num_part.split('.')] + except ValueError, e: + raise NotImplementedError( + 'Cannot parse "%s" portion of Bazaar version "%s"\n %s' + % (num_part, self._vcs_version(), str(e))) + cmps = [cmp(a,b) for a,b in zip(self._parsed_version, args)] + for c in cmps: + if c != 0: + return c + return 0 + def _vcs_get_user_id(self): # excerpted from bzrlib.builtins.cmd_whoami.run() try: @@ -160,7 +200,11 @@ class Bzr(base.VCS): cmd = bzrlib.builtins.cmd_ls() cmd.outf = StringIO.StringIO() try: - cmd.run(revision=revision, path=path, recursive=recursive) + if self.version_cmp(2,0,0) == 1: + cmd.run(revision=revision, path=path, recursive=recursive) + else: # Pre-2.0 Bazaar + cmd.run(revision=revision, path=path, + non_recursive=not recursive) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): raise base.InvalidPath(path, root=self.repo, revision=revision) -- cgit From 9ef5ba29e9fc2804784b7f33dde80000a16f43cb Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 28 Jan 2010 12:46:18 -0500 Subject: Fix bzrlib.builtins.cmd_cat() output for pre 1.6.0 bzrlibs. Fixed in bzr.dev revno: 3341.2.1 revision-id: bialix@ukr.net-20080407074826-5lwuyv4dn1qlijg4 parent: pqm@pqm.ubuntu.com-20080407044456-s1a9orh0kssphdh9 committer: Alexander Belchenko branch nick: cmd-cat timestamp: Mon 2008-04-07 10:48:26 +0300 message: `bzr cat` no more internally used Tree.print_file(). Merged into bzr.dev's trunk revno: 3512 [merge] revision-id: pqm@pqm.ubuntu.com-20080626004245-dnw85so4xqg8r9hy parent: pqm@pqm.ubuntu.com-20080625230724-lyux37pu8nx8tq34 parent: aaron@aaronbentley.com-20080626001706-wo3w74fwgliy12s4 committer: Canonical.com Patch Queue Manager branch nick: +trunk timestamp: Thu 2008-06-26 01:42:45 +0100 message: (bialix) Deprectate (Branch|Repository).print_file, fix cmd_cat Before bzr branch 1.6 bzr.dev$ bzr tags ... bzr-1.5rc1 3418.6.3 bzr-1.6 3606.5.9 ... Fixes: python test.py -q libbe.storage.vcs.bzr ...............................FSome value:1E.. ====================================================================== ERROR: Get should be able to return the previous version. ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/wking/src/fun/be/be.wtk/libbe/storage/base.py", line 976, in test_ get_previous_version ret = self.s.get(self.id, revision=revs[i]) File "/home/wking/src/fun/be/be.wtk/libbe/storage/base.py", line 335, in get value = self._get(*args, **kwargs) File "/home/wking/src/fun/be/be.wtk/libbe/storage/vcs/base.py", line 849, in _ get raise InvalidID(id, revision) InvalidID: unlikely id in revision 1 ... --- libbe/storage/vcs/bzr.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index b617d68..03a64f8 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -172,12 +172,20 @@ class Bzr(base.VCS): revision = self._parse_revision_string(revision) cmd = bzrlib.builtins.cmd_cat() cmd.outf = StringIO.StringIO() + if self.version_cmp(1,6,0) == -1: + # old bzrlib cmd_cat uses sys.stdout not self.outf for output. + stdout = sys.stdout + sys.stdout = cmd.outf try: cmd.run(filename=path, revision=revision) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): raise base.InvalidPath(path, root=self.repo, revision=revision) raise + finally: + if self.version_cmp(2,0,0) == -1: + cmd.outf = sys.stdout + sys.stdout = stdout return cmd.outf.getvalue() def _vcs_path(self, id, revision): @@ -200,7 +208,7 @@ class Bzr(base.VCS): cmd = bzrlib.builtins.cmd_ls() cmd.outf = StringIO.StringIO() try: - if self.version_cmp(2,0,0) == 1: + if self.version_cmp(2,0,0) >= 0: cmd.run(revision=revision, path=path, recursive=recursive) else: # Pre-2.0 Bazaar cmd.run(revision=revision, path=path, -- cgit From 7e120421446f88f9bde0674f57fb1667c5f70ebd Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 28 Jan 2010 13:06:06 -0500 Subject: Work around "bzr ls --non-recursive PATH : no list" bug in old bzrlib. See: https://bugs.launchpad.net/bzr/+bug/158690 Bug affected versions: 0.90.0 (reported) 1.3.1 (my test suite hit it) Doesn't affect versions: 2.0+ (non_recursive -> recursive) But I haven't isolated the source more specifically. Working around it for everything < 2.0 should be safe, but the cutoff could be fine-tuned if someone wants to dig through the bzr.dev history... --- libbe/storage/vcs/bzr.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 03a64f8..ce140bc 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -172,7 +172,7 @@ class Bzr(base.VCS): revision = self._parse_revision_string(revision) cmd = bzrlib.builtins.cmd_cat() cmd.outf = StringIO.StringIO() - if self.version_cmp(1,6,0) == -1: + if self.version_cmp(1,6,0) < 0: # old bzrlib cmd_cat uses sys.stdout not self.outf for output. stdout = sys.stdout sys.stdout = cmd.outf @@ -183,7 +183,7 @@ class Bzr(base.VCS): raise base.InvalidPath(path, root=self.repo, revision=revision) raise finally: - if self.version_cmp(2,0,0) == -1: + if self.version_cmp(2,0,0) < 0: cmd.outf = sys.stdout sys.stdout = stdout return cmd.outf.getvalue() @@ -210,15 +210,20 @@ class Bzr(base.VCS): try: if self.version_cmp(2,0,0) >= 0: cmd.run(revision=revision, path=path, recursive=recursive) - else: # Pre-2.0 Bazaar + else: + # Pre-2.0 Bazaar (non_recursive) + # + working around broken non_recursive+path implementation + # (https://bugs.launchpad.net/bzr/+bug/158690) cmd.run(revision=revision, path=path, - non_recursive=not recursive) + non_recursive=False) except bzrlib.errors.BzrCommandError, e: if 'not present in revision' in str(e): raise base.InvalidPath(path, root=self.repo, revision=revision) raise children = cmd.outf.getvalue().rstrip('\n').splitlines() children = [self._u_rel_path(c, path) for c in children] + if self.version_cmp(2,0,0) < 0 and recursive == False: + children = [c for c in children if os.path.sep not in c] return children def _vcs_commit(self, commitfile, allow_empty=False): -- cgit From 3e16a0ab627a095605f14a5164c2d8e14a3bcaa9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 1 Feb 2010 12:31:53 -0500 Subject: Made Bzr/Darcs.version_cmp() more robust in response to Chris' email. From: Chris Ball Subject: Test suite status ... I ran the wking@drexel.edu-20100130162439-pmh5tg6kuq92x3l5 testsuite on Fedora 13/Rawhide. Had to downgrade Mercurial (bzr-hg doesn't support 1.4.2 yet) and bzr (my Fedora package contained a "b4" in the version string, which breaks libbe/storage/vcs/bzr.py:version_cmp()). ... --- libbe/storage/vcs/bzr.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index ce140bc..01d9948 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -37,6 +37,7 @@ import re import shutil import StringIO import sys +import types import libbe import base @@ -82,22 +83,27 @@ class Bzr(base.VCS): >>> b._vcs_version = lambda : "2.0.0pre2" >>> b._parsed_version = None >>> b.version_cmp(3) + -1 + >>> b.version_cmp(2,0,1) Traceback (most recent call last): ... - NotImplementedError: Cannot parse "2.0.0pre2" portion of Bazaar version "2.0.0pre2" - invalid literal for int() with base 10: '0pre2' + NotImplementedError: Cannot parse non-integer portion "0pre2" of Bzr version "2.0.0pre2" """ if not hasattr(self, '_parsed_version') \ or self._parsed_version == None: num_part = self._vcs_version().split(' ')[0] - try: - self._parsed_version = [int(i) for i in num_part.split('.')] - except ValueError, e: + self._parsed_version = [] + for num in num_part.split('.'): + try: + self._parsed_version.append(int(num)) + except ValueError, e: + self._parsed_version.append(num) + for current,other in zip(self._parsed_version, args): + if type(current) != types.IntType: raise NotImplementedError( - 'Cannot parse "%s" portion of Bazaar version "%s"\n %s' - % (num_part, self._vcs_version(), str(e))) - cmps = [cmp(a,b) for a,b in zip(self._parsed_version, args)] - for c in cmps: + 'Cannot parse non-integer portion "%s" of Bzr version "%s"' + % (current, self._vcs_version())) + c = cmp(current,other) if c != 0: return c return 0 -- cgit From 977eff5af10b50ba6e6edb6abc4f40804c418b12 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 7 Feb 2010 17:53:53 -0500 Subject: Fixed docstrings so only Sphinx errors are "autosummary" and "missing attribute" --- libbe/storage/vcs/bzr.py | 118 ++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 53 deletions(-) (limited to 'libbe/storage/vcs/bzr.py') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 01d9948..5a62968 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -18,8 +18,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Bazaar (bzr) backend. +"""Bazaar_ (bzr) backend. + +.. _Bazaar: http://bazaar.canonical.com/ """ try: @@ -51,6 +52,8 @@ def new(): return Bzr() class Bzr(base.VCS): + """:class:`base.VCS` implementation for Bazaar. + """ name = 'bzr' client = None # bzrlib module @@ -64,12 +67,18 @@ class Bzr(base.VCS): return bzrlib.__version__ def version_cmp(self, *args): - """ - Compare the installed Bazaar version V_i with another version - V_o (given in *args). Returns - 1 if V_i > V_o, - 0 if V_i == V_o, and - -1 if V_i < V_o + """Compare the installed Bazaar version `V_i` with another version + `V_o` (given in `*args`). Returns + + === =============== + 1 if `V_i > V_o` + 0 if `V_i == V_o` + -1 if `V_i < V_o` + === =============== + + Examples + -------- + >>> b = Bzr(repo='.') >>> b._vcs_version = lambda : "2.3.1 (release)" >>> b.version_cmp(2,3,1) @@ -275,51 +284,54 @@ class Bzr(base.VCS): return cmd.outf.getvalue() def _parse_diff(self, diff_text): - """ - Example diff text: - - === modified file 'dir/changed' - --- dir/changed 2010-01-16 01:54:53 +0000 - +++ dir/changed 2010-01-16 01:54:54 +0000 - @@ -1,3 +1,3 @@ - hi - -there - +everyone and - joe - - === removed file 'dir/deleted' - --- dir/deleted 2010-01-16 01:54:53 +0000 - +++ dir/deleted 1970-01-01 00:00:00 +0000 - @@ -1,3 +0,0 @@ - -in - -the - -beginning - - === removed file 'dir/moved' - --- dir/moved 2010-01-16 01:54:53 +0000 - +++ dir/moved 1970-01-01 00:00:00 +0000 - @@ -1,4 +0,0 @@ - -the - -ants - -go - -marching - - === added file 'dir/moved2' - --- dir/moved2 1970-01-01 00:00:00 +0000 - +++ dir/moved2 2010-01-16 01:54:34 +0000 - @@ -0,0 +1,4 @@ - +the - +ants - +go - +marching - - === added file 'dir/new' - --- dir/new 1970-01-01 00:00:00 +0000 - +++ dir/new 2010-01-16 01:54:54 +0000 - @@ -0,0 +1,2 @@ - +hello - +world - + """_parse_diff(diff_text) -> (new,modified,removed) + + `new`, `modified`, and `removed` are lists of files. + + Example diff text:: + + === modified file 'dir/changed' + --- dir/changed 2010-01-16 01:54:53 +0000 + +++ dir/changed 2010-01-16 01:54:54 +0000 + @@ -1,3 +1,3 @@ + hi + -there + +everyone and + joe + + === removed file 'dir/deleted' + --- dir/deleted 2010-01-16 01:54:53 +0000 + +++ dir/deleted 1970-01-01 00:00:00 +0000 + @@ -1,3 +0,0 @@ + -in + -the + -beginning + + === removed file 'dir/moved' + --- dir/moved 2010-01-16 01:54:53 +0000 + +++ dir/moved 1970-01-01 00:00:00 +0000 + @@ -1,4 +0,0 @@ + -the + -ants + -go + -marching + + === added file 'dir/moved2' + --- dir/moved2 1970-01-01 00:00:00 +0000 + +++ dir/moved2 2010-01-16 01:54:34 +0000 + @@ -0,0 +1,4 @@ + +the + +ants + +go + +marching + + === added file 'dir/new' + --- dir/new 1970-01-01 00:00:00 +0000 + +++ dir/new 2010-01-16 01:54:54 +0000 + @@ -0,0 +1,2 @@ + +hello + +world + """ new = [] modified = [] -- cgit