Ross Light 15 лет назад
Родитель
Сommit
d3b58ac7c7

+ 23 - 0
NEWS

@@ -1,3 +1,26 @@
+0.6.1	UNRELEASED
+
+ BUG FIXES
+
+  * Fix memory leak in C implementation of sorted_tree_items. (Dave Borowitz)
+
+  * Use correct path separators for named repo files. (Dave Borowitz)
+
+ FEATURES
+
+  * Move named file initilization to BaseRepo. (Dave Borowitz)
+
+ TESTS
+
+  * Add tests for sorted_tree_items and C implementation. (Dave Borowitz)
+
+  * Add a MemoryRepo that stores everything in memory. (Dave Borowitz)
+
+ CLEANUP
+
+  * Clean up file headers. (Dave Borowitz)
+
+
 0.6.0	2010-05-22
 
 note: This list is most likely incomplete for 0.6.0.

+ 4 - 4
bin/dul-daemon

@@ -1,17 +1,17 @@
 #!/usr/bin/python
-# dul-daemon - Simple git-daemon a like
+# dul-daemon - Simple git-daemon-like server
 # Copyright (C) 2008 John Carr <john.carr@unrouted.co.uk>
-# 
+#
 # 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; version 2
 # or (at your option) a later version of the License.
-# 
+#
 # 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,

+ 3 - 3
bin/dul-receive-pack

@@ -1,17 +1,17 @@
 #!/usr/bin/python
 # dul-receive-pack - git-receive-pack in python
 # Copyright (C) 2008 John Carr <john.carr@unrouted.co.uk>
-# 
+#
 # 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; version 2
 # or (at your option) a later version of the License.
-# 
+#
 # 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,

+ 3 - 3
bin/dul-upload-pack

@@ -1,17 +1,17 @@
 #!/usr/bin/python
 # dul-upload-pack - git-upload-pack in python
 # Copyright (C) 2008 John Carr <john.carr@unrouted.co.uk>
-# 
+#
 # 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; version 2
 # or (at your option) a later version of the License.
-# 
+#
 # 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,

+ 1 - 1
bin/dul-web

@@ -1,6 +1,6 @@
 #!/usr/bin/python
 # dul-web - HTTP-based git server
-# Copyright (C) 2010 David Borowitz <dborowitz@google.com>
+# Copyright (C) 2010 Google, Inc. <dborowitz@google.com>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

+ 3 - 3
bin/dulwich

@@ -1,17 +1,17 @@
 #!/usr/bin/python
 # dulwich - Simple command-line interface to Dulwich
 # Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
-# 
+#
 # 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; version 2
 # or (at your option) a later version of the License.
-# 
+#
 # 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,

+ 4 - 4
dulwich/__init__.py

@@ -1,18 +1,18 @@
 # __init__.py -- The git module of dulwich
 # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
 # Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
-# 
+#
 # 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; version 2
-# of the License or (at your option) any later version of 
+# of the License or (at your option) any later version of
 # the License.
-# 
+#
 # 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,

+ 24 - 8
dulwich/_objects.c

@@ -136,6 +136,14 @@ int cmp_tree_item(const void *_a, const void *_b)
 	return strcmp(remain_a, remain_b);
 }
 
+static void free_tree_items(struct tree_item *items, int num) {
+	int i;
+	for (i = 0; i < num; i++) {
+		Py_DECREF(items[i].tuple);
+	}
+	free(items);
+}
+
 static PyObject *py_sorted_tree_items(PyObject *self, PyObject *entries)
 {
 	struct tree_item *qsort_entries;
@@ -159,10 +167,16 @@ static PyObject *py_sorted_tree_items(PyObject *self, PyObject *entries)
 	i = 0;
 	while (PyDict_Next(entries, &pos, &key, &value)) {
 		PyObject *py_mode, *py_int_mode, *py_sha;
-		
+
+		if (!PyString_Check(key)) {
+			PyErr_SetString(PyExc_TypeError, "Name is not a string");
+			free_tree_items(qsort_entries, i);
+			return NULL;
+		}
+
 		if (PyTuple_Size(value) != 2) {
 			PyErr_SetString(PyExc_ValueError, "Tuple has invalid size");
-			free(qsort_entries);
+			free_tree_items(qsort_entries, i);
 			return NULL;
 		}
 
@@ -170,19 +184,21 @@ static PyObject *py_sorted_tree_items(PyObject *self, PyObject *entries)
 		py_int_mode = PyNumber_Int(py_mode);
 		if (!py_int_mode) {
 			PyErr_SetString(PyExc_TypeError, "Mode is not an integral type");
-			free(qsort_entries);
+			free_tree_items(qsort_entries, i);
 			return NULL;
 		}
 
 		py_sha = PyTuple_GET_ITEM(value, 1);
-		if (!PyString_CheckExact(key)) {
-			PyErr_SetString(PyExc_TypeError, "Name is not a string");
-			free(qsort_entries);
+		if (!PyString_Check(py_sha)) {
+			PyErr_SetString(PyExc_TypeError, "SHA is not a string");
+			Py_DECREF(py_int_mode);
+			free_tree_items(qsort_entries, i);
 			return NULL;
 		}
 		qsort_entries[i].name = PyString_AS_STRING(key);
 		qsort_entries[i].mode = PyInt_AS_LONG(py_mode);
-		qsort_entries[i].tuple = PyTuple_Pack(3, key, py_mode, py_sha);
+		qsort_entries[i].tuple = PyTuple_Pack(3, key, py_int_mode, py_sha);
+		Py_DECREF(py_int_mode);
 		i++;
 	}
 
@@ -190,7 +206,7 @@ static PyObject *py_sorted_tree_items(PyObject *self, PyObject *entries)
 
 	ret = PyList_New(num);
 	if (ret == NULL) {
-		free(qsort_entries);
+		free_tree_items(qsort_entries, i);
 		PyErr_NoMemory();
 		return NULL;
 	}

+ 41 - 38
dulwich/client.py

@@ -21,13 +21,11 @@
 
 __docformat__ = 'restructuredText'
 
-import os
 import select
 import socket
 import subprocess
 
 from dulwich.errors import (
-    ChecksumMismatch,
     SendPackError,
     UpdateRefsError,
     )
@@ -88,6 +86,45 @@ class GitClient(object):
             refs[ref] = sha
         return refs, server_capabilities
 
+    def _parse_status_report(self):
+        unpack = self.proto.read_pkt_line().strip()
+        if unpack != 'unpack ok':
+            st = True
+            # flush remaining error data
+            while st is not None:
+                st = self.proto.read_pkt_line()
+            raise SendPackError(unpack)
+        statuses = []
+        errs = False
+        ref_status = self.proto.read_pkt_line()
+        while ref_status:
+            ref_status = ref_status.strip()
+            statuses.append(ref_status)
+            if not ref_status.startswith('ok '):
+                errs = True
+            ref_status = self.proto.read_pkt_line()
+
+        if errs:
+            ref_status = {}
+            ok = set()
+            for status in statuses:
+                if ' ' not in status:
+                    # malformed response, move on to the next one
+                    continue
+                status, ref = status.split(' ', 1)
+
+                if status == 'ng':
+                    if ' ' in ref:
+                        ref, status = ref.split(' ', 1)
+                else:
+                    ok.add(ref)
+                ref_status[ref] = status
+            raise UpdateRefsError('%s failed to update' %
+                                  ', '.join([ref for ref in ref_status
+                                             if ref not in ok]),
+                                  ref_status=ref_status)
+
+
     # TODO(durin42): add side-band-64k capability support here and advertise it
     def send_pack(self, path, determine_wants, generate_pack_contents):
         """Upload a pack to a remote repository.
@@ -132,42 +169,7 @@ class GitClient(object):
                                          len(objects))
 
         if 'report-status' in self._send_capabilities:
-            unpack = self.proto.read_pkt_line().strip()
-            if unpack != 'unpack ok':
-                st = True
-                # flush remaining error data
-                while st is not None:
-                    st = self.proto.read_pkt_line()
-                raise SendPackError(unpack)
-            statuses = []
-            errs = False
-            ref_status = self.proto.read_pkt_line()
-            while ref_status:
-                ref_status = ref_status.strip()
-                statuses.append(ref_status)
-                if not ref_status.startswith('ok '):
-                    errs = True
-                ref_status = self.proto.read_pkt_line()
-
-            if errs:
-                ref_status = {}
-                ok = set()
-                for status in statuses:
-                    if ' ' not in status:
-                        # malformed response, move on to the next one
-                        continue
-                    status, ref = status.split(' ', 1)
-
-                    if status == 'ng':
-                        if ' ' in ref:
-                            ref, status = ref.split(' ', 1)
-                    else:
-                        ok.add(ref)
-                    ref_status[ref] = status
-                raise UpdateRefsError('%s failed to update' %
-                                      ', '.join([ref for ref in ref_status
-                                                 if ref not in ok]),
-                                      ref_status=ref_status)
+            self._parse_status_report()
         # wait for EOF before returning
         data = self.proto.read()
         if data:
@@ -402,5 +404,6 @@ def get_transport_and_path(uri):
         if uri.startswith(handler):
             host, path = uri[len(handler):].split("/", 1)
             return transport(host), "/"+path
+    # FIXME: Parse rsync-like git URLs (user@host:/path), bug 568493
     # if its not git or git+ssh, try a local url..
     return SubprocessGitClient(), uri

+ 4 - 4
dulwich/fastexport.py

@@ -1,17 +1,17 @@
 # __init__.py -- Fast export/import functionality
 # Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
-# 
+#
 # 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; version 2
-# of the License or (at your option) any later version of 
+# of the License or (at your option) any later version of
 # the License.
-# 
+#
 # 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,

+ 4 - 4
dulwich/index.py

@@ -1,16 +1,16 @@
-# index.py -- File parser/write for the git index file
+# index.py -- File parser/writer for the git index file
 # Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
- 
+#
 # 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; version 2
 # of the License or (at your opinion) any later version of the license.
-# 
+#
 # 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,

+ 1 - 0
dulwich/lru_cache.py

@@ -1,3 +1,4 @@
+# lru_cache.py -- Simple LRU cache for dulwich
 # Copyright (C) 2006, 2008 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify

+ 4 - 3
dulwich/misc.py

@@ -1,20 +1,21 @@
 # misc.py -- For dealing with python2.4 oddness
 # Copyright (C) 2008 Canonical Ltd.
-# 
+#
 # 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; version 2
 # of the License or (at your option) a 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.
+
 """Misc utilities to work with python <2.6.
 
 These utilities can all be deleted when dulwich decides it wants to stop

+ 7 - 3
dulwich/objects.py

@@ -17,7 +17,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 """Access to base git objects."""
 
 
@@ -706,10 +705,15 @@ def sorted_tree_items(entries):
     the items would be serialized.
 
     :param entries: Dictionary mapping names to (mode, sha) tuples
-    :return: Iterator over (name, mode, sha)
+    :return: Iterator over (name, mode, hexsha)
     """
     for name, entry in sorted(entries.iteritems(), cmp=cmp_entry):
-        yield name, entry[0], entry[1]
+        mode, hexsha = entry
+        # Stricter type checks than normal to mirror checks in the C version.
+        mode = int(mode)
+        if not isinstance(hexsha, str):
+            raise TypeError('Expected a string for SHA, got %r' % hexsha)
+        yield name, mode, hexsha
 
 
 def cmp_entry((name1, value1), (name2, value2)):

+ 2 - 2
dulwich/pack.py

@@ -1,6 +1,6 @@
-# pack.py -- For dealing wih packed git objects.
+# pack.py -- For dealing with packed git objects.
 # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
-# Copryight (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

+ 5 - 5
dulwich/patch.py

@@ -1,16 +1,16 @@
-# patch.py -- For dealing wih packed-style patches.
-# Copryight (C) 2009 Jelmer Vernooij <jelmer@samba.org>
-# 
+# patch.py -- For dealing with packed-style patches.
+# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
+#
 # 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; version 2
 # of the License or (at your option) a 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,

+ 1 - 1
dulwich/protocol.py

@@ -1,5 +1,5 @@
 # protocol.py -- Shared parts of the git protocols
-# Copryight (C) 2008 John Carr <john.carr@unrouted.co.uk>
+# Copyright (C) 2008 John Carr <john.carr@unrouted.co.uk>
 # Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
 #
 # This program is free software; you can redistribute it and/or

+ 81 - 12
dulwich/repo.py

@@ -1,4 +1,4 @@
-# repo.py -- For dealing wih git repositories.
+# repo.py -- For dealing with git repositories.
 # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
 # Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
 #
@@ -21,7 +21,7 @@
 
 """Repository access."""
 
-
+from cStringIO import StringIO
 import errno
 import os
 
@@ -42,6 +42,7 @@ from dulwich.file import (
     )
 from dulwich.object_store import (
     DiskObjectStore,
+    MemoryObjectStore,
     )
 from dulwich.objects import (
     Blob,
@@ -750,6 +751,16 @@ class BaseRepo(object):
         self.object_store = object_store
         self.refs = refs
 
+    def _init_files(self):
+        """Initialize a default set of named files."""
+        self._put_named_file('description', "Unnamed repository")
+        self._put_named_file('config', ('[core]\n'
+                                        'repositoryformatversion = 0\n'
+                                        'filemode = true\n'
+                                        'bare = false\n'
+                                        'logallrefupdates = true\n'))
+        self._put_named_file(os.path.join('info', 'exclude'), '')
+
     def get_named_file(self, path):
         """Get a file from the control dir with a specific name.
 
@@ -762,6 +773,14 @@ class BaseRepo(object):
         """
         raise NotImplementedError(self.get_named_file)
 
+    def _put_named_file(self, path, contents):
+        """Write a file to the control dir with the given name and contents.
+
+        :param path: The path to the file, relative to the control dir.
+        :contents: A string to write to the file.
+        """
+        raise NotImplementedError(self._put_named_file)
+
     def open_index(self):
         """Open the index for this repository.
 
@@ -1072,8 +1091,12 @@ class Repo(BaseRepo):
         return self._controldir
 
     def _put_named_file(self, path, contents):
-        """Write a file from the control dir with a specific name and contents.
+        """Write a file to the control dir with the given name and contents.
+
+        :param path: The path to the file, relative to the control dir.
+        :contents: A string to write to the file.
         """
+        path = path.lstrip(os.path.sep)
         f = GitFile(os.path.join(self.controldir(), path), 'wb')
         try:
             f.write(contents)
@@ -1090,8 +1113,11 @@ class Repo(BaseRepo):
         :param path: The path to the file, relative to the control dir.
         :return: An open file object, or None if the file does not exist.
         """
+        # TODO(dborowitz): sanitize filenames, since this is used directly by
+        # the dumb web serving code.
+        path = path.lstrip(os.path.sep)
         try:
-            return open(os.path.join(self.controldir(), path.lstrip('/')), 'rb')
+            return open(os.path.join(self.controldir(), path), 'rb')
         except (IOError, OSError), e:
             if e.errno == errno.ENOENT:
                 return None
@@ -1162,14 +1188,57 @@ class Repo(BaseRepo):
         DiskObjectStore.init(os.path.join(path, OBJECTDIR))
         ret = cls(path)
         ret.refs.set_symbolic_ref("HEAD", "refs/heads/master")
-        ret._put_named_file('description', "Unnamed repository")
-        ret._put_named_file('config', """[core]
-    repositoryformatversion = 0
-    filemode = true
-    bare = false
-    logallrefupdates = true
-""")
-        ret._put_named_file(os.path.join('info', 'exclude'), '')
+        ret._init_files()
         return ret
 
     create = init_bare
+
+
+class MemoryRepo(BaseRepo):
+    """Repo that stores refs, objects, and named files in memory.
+
+    MemoryRepos are always bare: they have no working tree and no index, since
+    those have a stronger dependency on the filesystem.
+    """
+
+    def __init__(self):
+        BaseRepo.__init__(self, MemoryObjectStore(), DictRefsContainer({}))
+        self._named_files = {}
+        self.bare = True
+
+    def _put_named_file(self, path, contents):
+        """Write a file to the control dir with the given name and contents.
+
+        :param path: The path to the file, relative to the control dir.
+        :contents: A string to write to the file.
+        """
+        self._named_files[path] = contents
+
+    def get_named_file(self, path):
+        """Get a file from the control dir with a specific name.
+
+        Although the filename should be interpreted as a filename relative to
+        the control dir in a disk-baked Repo, the object returned need not be
+        pointing to a file in that location.
+
+        :param path: The path to the file, relative to the control dir.
+        :return: An open file object, or None if the file does not exist.
+        """
+        contents = self._named_files.get(path, None)
+        if contents is None:
+            return None
+        return StringIO(contents)
+
+    def open_index(self):
+        """Fail to open index for this repo, since it is bare."""
+        raise NoIndexPresent()
+
+    @classmethod
+    def init_bare(cls, objects, refs):
+        ret = cls()
+        for obj in objects:
+            ret.object_store.add_object(obj)
+        for refname, sha in refs.iteritems():
+            ret.refs[refname] = sha
+        ret._init_files()
+        return ret

+ 1 - 1
dulwich/server.py

@@ -1,5 +1,5 @@
 # server.py -- Implementation of the server side git protocols
-# Copryight (C) 2008 John Carr <john.carr@unrouted.co.uk>
+# Copyright (C) 2008 John Carr <john.carr@unrouted.co.uk>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

+ 4 - 4
dulwich/tests/__init__.py

@@ -1,17 +1,17 @@
 # __init__.py -- The tests for dulwich
 # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
-# 
+#
 # 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; version 2
-# of the License or (at your option) any later version of 
+# of the License or (at your option) any later version of
 # the License.
-# 
+#
 # 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,

+ 2 - 2
dulwich/tests/compat/test_pack.py

@@ -1,4 +1,4 @@
-# test_pack.py -- Compatibilty tests for git packs.
+# test_pack.py -- Compatibility tests for git packs.
 # Copyright (C) 2010 Google, Inc.
 #
 # This program is free software; you can redistribute it and/or
@@ -17,7 +17,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-"""Compatibilty tests for git packs."""
+"""Compatibility tests for git packs."""
 
 
 import binascii

+ 2 - 2
dulwich/tests/compat/test_server.py

@@ -1,4 +1,4 @@
-# test_server.py -- Compatibilty tests for git server.
+# test_server.py -- Compatibility tests for git server.
 # Copyright (C) 2010 Google, Inc.
 #
 # This program is free software; you can redistribute it and/or
@@ -17,7 +17,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-"""Compatibilty tests between Dulwich and the cgit server.
+"""Compatibility tests between Dulwich and the cgit server.
 
 Warning: these tests should be fairly stable, but when writing/debugging new
 tests, deadlocks may freeze the test process such that it cannot be Ctrl-C'ed.

+ 2 - 2
dulwich/tests/compat/test_web.py

@@ -1,4 +1,4 @@
-# test_web.py -- Compatibilty tests for the git web server.
+# test_web.py -- Compatibility tests for the git web server.
 # Copyright (C) 2010 Google, Inc.
 #
 # This program is free software; you can redistribute it and/or
@@ -17,7 +17,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-"""Compatibilty tests between Dulwich and the cgit HTTP server.
+"""Compatibility tests between Dulwich and the cgit HTTP server.
 
 Warning: these tests should be fairly stable, but when writing/debugging new
 tests, deadlocks may freeze the test process such that it cannot be Ctrl-C'ed.

+ 4 - 4
dulwich/tests/test_fastexport.py

@@ -1,17 +1,17 @@
 # test_fastexport.py -- Fast export/import functionality
 # Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
-# 
+#
 # 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; version 2
-# of the License or (at your option) any later version of 
+# of the License or (at your option) any later version of
 # the License.
-# 
+#
 # 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,

+ 0 - 1
dulwich/tests/test_file.py

@@ -16,7 +16,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 import errno
 import os
 import shutil

+ 0 - 1
dulwich/tests/test_index.py

@@ -16,7 +16,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 """Tests for the index."""
 
 

+ 0 - 1
dulwich/tests/test_object_store.py

@@ -16,7 +16,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 """Tests for the object store interface."""
 
 

+ 48 - 9
dulwich/tests/test_objects.py

@@ -1,23 +1,22 @@
 # test_objects.py -- tests for objects.py
 # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
-# 
+#
 # 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; version 2
-# of the License or (at your option) any later version of 
+# of the License or (at your option) any later version of
 # the License.
-# 
+#
 # 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.
 
-
 """Tests for git base objects."""
 
 # TODO: Round-trip parse-serialize-parse and serialize-parse-serialize tests.
@@ -46,6 +45,8 @@ from dulwich.objects import (
     parse_timezone,
     parse_tree,
     _parse_tree_py,
+    sorted_tree_items,
+    _sorted_tree_items_py,
     )
 from dulwich.tests import (
     TestSkipped,
@@ -404,6 +405,19 @@ class CommitParseTests(ShaFileCheckTests):
                 self.assertCheckFails(Commit, text)
 
 
+_TREE_ITEMS = {
+  'a.c': (0100755, 'd80c186a03f423a81b39df39dc87fd269736ca86'),
+  'a': (stat.S_IFDIR, 'd80c186a03f423a81b39df39dc87fd269736ca86'),
+  'a/c': (stat.S_IFDIR, 'd80c186a03f423a81b39df39dc87fd269736ca86'),
+  }
+
+_SORTED_TREE_ITEMS = [
+  ('a.c', 0100755, 'd80c186a03f423a81b39df39dc87fd269736ca86'),
+  ('a', stat.S_IFDIR, 'd80c186a03f423a81b39df39dc87fd269736ca86'),
+  ('a/c', stat.S_IFDIR, 'd80c186a03f423a81b39df39dc87fd269736ca86'),
+  ]
+
+
 class TreeTests(ShaFileCheckTests):
 
     def test_simple(self):
@@ -422,10 +436,9 @@ class TreeTests(ShaFileCheckTests):
 
     def test_tree_dir_sort(self):
         x = Tree()
-        x["a.c"] = (0100755, "d80c186a03f423a81b39df39dc87fd269736ca86")
-        x["a"] = (stat.S_IFDIR, "d80c186a03f423a81b39df39dc87fd269736ca86")
-        x["a/c"] = (stat.S_IFDIR, "d80c186a03f423a81b39df39dc87fd269736ca86")
-        self.assertEquals(["a.c", "a", "a/c"], [p[0] for p in x.iteritems()])
+        for name, item in _TREE_ITEMS.iteritems():
+            x[name] = item
+        self.assertEquals(_SORTED_TREE_ITEMS, list(x.iteritems()))
 
     def _do_test_parse_tree(self, parse_tree):
         dir = os.path.join(os.path.dirname(__file__), 'data', 'trees')
@@ -441,6 +454,32 @@ class TreeTests(ShaFileCheckTests):
             raise TestSkipped('parse_tree extension not found')
         self._do_test_parse_tree(parse_tree)
 
+    def _do_test_sorted_tree_items(self, sorted_tree_items):
+        def do_sort(entries):
+            return list(sorted_tree_items(entries))
+
+        self.assertEqual(_SORTED_TREE_ITEMS, do_sort(_TREE_ITEMS))
+
+        # C/Python implementations may differ in specific error types, but
+        # should all error on invalid inputs.
+        # For example, the C implementation has stricter type checks, so may
+        # raise TypeError where the Python implementation raises AttributeError.
+        errors = (TypeError, ValueError, AttributeError)
+        self.assertRaises(errors, do_sort, 'foo')
+        self.assertRaises(errors, do_sort, {'foo': (1, 2, 3)})
+
+        myhexsha = 'd80c186a03f423a81b39df39dc87fd269736ca86'
+        self.assertRaises(errors, do_sort, {'foo': ('xxx', myhexsha)})
+        self.assertRaises(errors, do_sort, {'foo': (0100755, 12345)})
+
+    def test_sorted_tree_items(self):
+        self._do_test_sorted_tree_items(_sorted_tree_items_py)
+
+    def test_sorted_tree_items_extension(self):
+        if sorted_tree_items is _sorted_tree_items_py:
+            raise TestSkipped('sorted_tree_items extension not found')
+        self._do_test_sorted_tree_items(sorted_tree_items)
+
     def test_check(self):
         t = Tree
         sha = hex_to_sha(a_sha)

+ 0 - 1
dulwich/tests/test_pack.py

@@ -17,7 +17,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 """Tests for Dulwich packs."""
 
 

+ 4 - 4
dulwich/tests/test_patch.py

@@ -1,16 +1,16 @@
 # test_patch.py -- tests for patch.py
-# Copryight (C) 2010 Jelmer Vernooij <jelmer@samba.org>
-# 
+# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
+#
 # 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; version 2
 # of the License or (at your option) a 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,

+ 3 - 4
dulwich/tests/test_protocol.py

@@ -1,22 +1,21 @@
 # test_protocol.py -- Tests for the git protocol
 # Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
-# 
+#
 # 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; version 2
 # or (at your option) any later version of the License.
-# 
+#
 # 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.
 
-
 """Tests for the smart protocol utility functions."""
 
 

+ 23 - 2
dulwich/tests/test_repository.py

@@ -17,7 +17,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 """Tests for the repository."""
 
 from cStringIO import StringIO
@@ -36,6 +35,7 @@ from dulwich.repo import (
     check_ref_format,
     DictRefsContainer,
     Repo,
+    MemoryRepo,
     read_packed_refs,
     read_packed_refs_with_peeled,
     write_packed_refs,
@@ -51,14 +51,35 @@ missing_sha = 'b91fa4d900e17e99b433218e988c4eb4a3e9a097'
 
 class CreateRepositoryTests(unittest.TestCase):
 
-    def test_create(self):
+    def assertFileContentsEqual(self, expected, repo, path):
+        f = repo.get_named_file(path)
+        if not f:
+            self.assertEqual(expected, None)
+        else:
+            try:
+                self.assertEqual(expected, f.read())
+            finally:
+                f.close()
+
+    def _check_repo_contents(self, repo):
+        self.assertTrue(repo.bare)
+        self.assertFileContentsEqual('Unnamed repository', repo, 'description')
+        self.assertFileContentsEqual('', repo, os.path.join('info', 'exclude'))
+        self.assertFileContentsEqual(None, repo, 'nonexistent file')
+
+    def test_create_disk(self):
         tmp_dir = tempfile.mkdtemp()
         try:
             repo = Repo.init_bare(tmp_dir)
             self.assertEquals(tmp_dir, repo._controldir)
+            self._check_repo_contents(repo)
         finally:
             shutil.rmtree(tmp_dir)
 
+    def test_create_memory(self):
+        repo = MemoryRepo.init_bare([], {})
+        self._check_repo_contents(repo)
+
 
 class RepositoryTests(unittest.TestCase):
 

+ 0 - 1
dulwich/tests/test_server.py

@@ -16,7 +16,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
-
 """Tests for the smart protocol server."""
 
 

+ 1 - 1
dulwich/tests/test_web.py

@@ -1,5 +1,5 @@
 # test_web.py -- Tests for the git HTTP server
-# Copryight (C) 2010 Google, Inc.
+# Copyright (C) 2010 Google, Inc.
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

+ 12 - 4
dulwich/web.py

@@ -1,5 +1,5 @@
 # web.py -- WSGI smart-http server
-# Copryight (C) 2010 Google, Inc.
+# Copyright (C) 2010 Google, Inc.
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -19,6 +19,7 @@
 """HTTP server for dulwich that implements the git smart HTTP protocol."""
 
 from cStringIO import StringIO
+import os
 import re
 import time
 
@@ -98,9 +99,14 @@ def send_file(req, f, content_type):
         raise
 
 
+def _url_to_path(url):
+    return url.replace('/', os.path.sep)
+
+
 def get_text_file(req, backend, mat):
     req.nocache()
-    return send_file(req, get_repo(backend, mat).get_named_file(mat.group()),
+    path = _url_to_path(mat.group())
+    return send_file(req, get_repo(backend, mat).get_named_file(path),
                      'text/plain')
 
 
@@ -121,13 +127,15 @@ def get_loose_object(req, backend, mat):
 
 def get_pack_file(req, backend, mat):
     req.cache_forever()
-    return send_file(req, get_repo(backend, mat).get_named_file(mat.group()),
+    path = _url_to_path(mat.group())
+    return send_file(req, get_repo(backend, mat).get_named_file(path),
                      'application/x-git-packed-objects')
 
 
 def get_idx_file(req, backend, mat):
     req.cache_forever()
-    return send_file(req, get_repo(backend, mat).get_named_file(mat.group()),
+    path = _url_to_path(mat.group())
+    return send_file(req, get_repo(backend, mat).get_named_file(path),
                      'application/x-git-packed-objects-toc')
 
 

+ 1 - 1
setup.py

@@ -1,5 +1,5 @@
 #!/usr/bin/python
-# Setup file for bzr-git
+# Setup file for dulwich
 # Copyright (C) 2008-2010 Jelmer Vernooij <jelmer@samba.org>
 
 try: