Browse Source

New upstream version 0.19.5

Jelmer Vernooij 6 years ago
parent
commit
6420170f35

+ 0 - 7
.coveragerc

@@ -1,7 +0,0 @@
-[run]
-branch = True
-source = dulwich
-
-[report]
-exclude_lines =
-    raise NotImplementedError

+ 0 - 23
.gitignore

@@ -1,23 +0,0 @@
-_trial_temp
-build
-build-pypy
-MANIFEST
-dist
-apidocs
-*,cover
-.testrepository
-*.pyc
-*.pyd
-*.pyo
-*.so
-*~
-*.swp
-*.swh
-*.swn
-*.swo
-docs/tutorial/index.html
-dulwich.egg-info/
-.tox/
-.idea/
-.coverage
-htmlcov/

+ 0 - 23
.mailmap

@@ -1,23 +0,0 @@
-Jelmer Vernooij <jelmer@jelmer.uk>
-Jelmer Vernooij <jelmer@jelmer.uk> <jelmer@jelmer.uk>
-Jelmer Vernooij <jelmer@jelmer.uk> <jelmer@samba.org>
-Jelmer Vernooij <jelmer@jelmer.uk> <jelmer@debian.org>
-Jelmer Vernooij <jelmer@jelmer.uk> <jelmer@canonical.com>
-Jelmer Vernooij <jelmer@jelmer.uk> <jelmer@google.com>
-Martin <gzlist@googlemail.com> <martin.packman@canonical.com>
-Dave Borowitz <dborowitz@google.com> <ddborowitz@gmail.com>
-Dave Borowitz <dborowitz@google.com> <dborowitz@google.com>
-John Carr <john.carr@unrouted.co.uk>
-Mark Mikofski <bwanamarko@yahoo.com> <mark.mikofski@sunpowercorp.com>
-Mark Mikofski <bwanamarko@yahoo.com> <bwana.marko@yahoo.com>
-David Carr <david@carrclan.us>
-Jon Bain <jsbain@yahoo.com> <jsbain@yahoo.com>
-James Westby <jw+debian@jameswestby.net> <jw+debian@jameswestby.net>
-David Keijser <david.keijser@klarna.com> <keijser@gmail.com>
-Benoît HERVIER <khertan@khertan.net> <khertan@khertan.net>
-Ryan Faulkner <rfaulk@yahoo-inc.com> <rfaulkner@wikimedia.org>
-David Bennett <davbennett@google.com> <david@dbinit.com>
-Risto Kankkunen <risto.kankkunen@iki.fi> <risto.kankkunen@f-secure.com>
-Augie Fackler <durin42@gmail.com> <raf@durin42.com>
-Damien Tournoud <damien@commerceguys.com> <damien@platform.sh>
-Marcin Kuźmiński <marcin@python-blog.com> <marcin@python-works.com>

+ 14 - 0
NEWS

@@ -1,3 +1,17 @@
+0.19.5	2018-08-08
+
+ IMPROVEMENTS
+
+  * Add ``porcelain.describe``. (Sylvia van Os)
+
+ BUG FIXES
+
+  * Fix regression in ``dulwich.porcelain.clone`` that prevented cloning
+    of remote repositories. (Jelmer Vernooij, #639)
+
+  * Don't leave around empty parent directories for removed refs.
+    (Damien Tournoud, #640)
+
 0.19.4	2018-06-24
 
  IMPROVEMENTS

+ 1 - 1
PKG-INFO

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: dulwich
-Version: 0.19.4
+Version: 0.19.5
 Summary: [![Build Status](https://travis-ci.org/dulwich/dulwich.png?branch=master)](https://travis-ci.org/dulwich/dulwich)
 [![Windows Build status](https://ci.appveyor.com/api/projects/status/mob7g4vnrfvvoweb?svg=true)](https://ci.appveyor.com/project/jelmer/dulwich/branch/master)
 

+ 9 - 0
bin/dulwich

@@ -614,6 +614,14 @@ class cmd_ls_files(Command):
             print(name)
 
 
+class cmd_describe(Command):
+
+    def run(self, args):
+        parser = optparse.OptionParser()
+        options, args = parser.parse_args(args)
+        print(porcelain.describe('.'))
+
+
 class cmd_help(Command):
 
     def run(self, args):
@@ -644,6 +652,7 @@ commands = {
     "clone": cmd_clone,
     "commit": cmd_commit,
     "commit-tree": cmd_commit_tree,
+    "describe": cmd_describe,
     "daemon": cmd_daemon,
     "diff": cmd_diff,
     "diff-tree": cmd_diff_tree,

+ 0 - 21
build.cmd

@@ -1,21 +0,0 @@
-@echo off
-:: To build extensions for 64 bit Python 3, we need to configure environment
-:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
-:: MS Windows SDK for Windows 7 and .NET Framework 4
-::
-:: More details at:
-:: https://github.com/cython/cython/wiki/CythonExtensionsOnWindows
-
-IF "%DISTUTILS_USE_SDK%"=="1" (
-    ECHO Configuring environment to build with MSVC on a 64bit architecture
-    ECHO Using Windows SDK 7.1
-    "C:\Program Files\Microsoft SDKs\Windows\v7.1\Setup\WindowsSdkVer.exe" -q -version:v7.1
-    CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 /release
-    SET MSSdk=1
-    REM Need the following to allow tox to see the SDK compiler
-    SET TOX_TESTENV_PASSENV=DISTUTILS_USE_SDK MSSdk INCLUDE LIB
-) ELSE (
-    ECHO Using default MSVC build environment
-)
-
-CALL %*

+ 0 - 15
devscripts/PREAMBLE.c

@@ -1,15 +0,0 @@
- * Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
- * General Public License as public by the Free Software Foundation; version 2.0
- * or (at your option) any later version. You can redistribute it and/or
- * modify it under the terms of either of these two licenses.
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * You should have received a copy of the licenses; if not, see
- * <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
- * and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
- * License, Version 2.0.

+ 0 - 16
devscripts/PREAMBLE.py

@@ -1,16 +0,0 @@
-# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
-# General Public License as public by the Free Software Foundation; version 2.0
-# or (at your option) any later version. You can redistribute it and/or
-# modify it under the terms of either of these two licenses.
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# You should have received a copy of the licenses; if not, see
-# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
-# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
-# License, Version 2.0.
-#

+ 0 - 3
devscripts/replace-preamble.sh

@@ -1,3 +0,0 @@
-#!/usr/bin/zsh
-perl -i -p0e "s{\Q$(cat PREAMBLE.py.old)\E}{$(cat devscripts/PREAMBLE.py)}g" dulwich/**/*.py bin/dul*
-perl -i -p0e "s{\Q$(cat PREAMBLE.c.old)\E}{$(cat devscripts/PREAMBLE.c)}g" dulwich/*.c

+ 0 - 2
docs/tutorial/.gitignore

@@ -1,2 +0,0 @@
-*.html
-myrepo

+ 1 - 1
dulwich.egg-info/PKG-INFO

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: dulwich
-Version: 0.19.4
+Version: 0.19.5
 Summary: [![Build Status](https://travis-ci.org/dulwich/dulwich.png?branch=master)](https://travis-ci.org/dulwich/dulwich)
 [![Windows Build status](https://ci.appveyor.com/api/projects/status/mob7g4vnrfvvoweb?svg=true)](https://ci.appveyor.com/project/jelmer/dulwich/branch/master)
 

+ 0 - 10
dulwich.egg-info/SOURCES.txt

@@ -1,6 +1,3 @@
-.coveragerc
-.gitignore
-.mailmap
 .testr.conf
 .travis.yml
 AUTHORS
@@ -13,25 +10,19 @@ README.md
 README.swift.md
 TODO
 appveyor.yml
-build.cmd
 dulwich.cfg
-requirements.txt
 setup.cfg
 setup.py
 tox.ini
 bin/dul-receive-pack
 bin/dul-upload-pack
 bin/dulwich
-devscripts/PREAMBLE.c
-devscripts/PREAMBLE.py
-devscripts/replace-preamble.sh
 docs/Makefile
 docs/conf.py
 docs/index.txt
 docs/make.bat
 docs/performance.txt
 docs/protocol.txt
-docs/tutorial/.gitignore
 docs/tutorial/Makefile
 docs/tutorial/conclusion.txt
 docs/tutorial/encoding.txt
@@ -81,7 +72,6 @@ dulwich.egg-info/SOURCES.txt
 dulwich.egg-info/dependency_links.txt
 dulwich.egg-info/requires.txt
 dulwich.egg-info/top_level.txt
-dulwich/contrib/README.md
 dulwich/contrib/__init__.py
 dulwich/contrib/paramiko_vendor.py
 dulwich/contrib/release_robot.py

+ 1 - 1
dulwich/__init__.py

@@ -22,4 +22,4 @@
 
 """Python implementation of the Git file formats and protocols."""
 
-__version__ = (0, 19, 4)
+__version__ = (0, 19, 5)

+ 0 - 3
dulwich/contrib/README.md

@@ -1,3 +0,0 @@
-This directory contains code that some may find useful. Code here is not an official
-part of Dulwich, and may no longer work. Unlike the rest of Dulwich, it is not regularly
-tested.

+ 73 - 5
dulwich/porcelain.py

@@ -30,6 +30,7 @@ Currently implemented:
  * commit
  * commit-tree
  * daemon
+ * describe
  * diff-tree
  * fetch
  * init
@@ -61,6 +62,7 @@ from contextlib import (
     contextmanager,
 )
 from io import BytesIO
+import datetime
 import os
 import posixpath
 import stat
@@ -306,11 +308,8 @@ def clone(source, target=None, bare=False, checkout=None,
     if checkout and bare:
         raise ValueError("checkout and bare are incompatible")
 
-    config = StackedConfig.default()
-    client, host_path = get_transport_and_path(source, config=config, **kwargs)
-
     if target is None:
-        target = host_path.split("/")[-1]
+        target = source.split("/")[-1]
 
     if not os.path.exists(target):
         os.mkdir(target)
@@ -322,7 +321,7 @@ def clone(source, target=None, bare=False, checkout=None,
 
     reflog_message = b'clone: from ' + source.encode('utf-8')
     try:
-        fetch_result = fetch(r, host_path, origin, message=reflog_message)
+        fetch_result = fetch(r, source, origin, message=reflog_message)
         target_config = r.get_config()
         if not isinstance(source, bytes):
             source = source.encode(DEFAULT_ENCODING)
@@ -1289,3 +1288,72 @@ def ls_files(repo):
     """List all files in an index."""
     with open_repo_closing(repo) as r:
         return sorted(r.open_index())
+
+
+def describe(repo):
+    """Describe the repository version.
+
+    :param projdir: git repository root
+    :returns: a string description of the current git revision
+
+    Examples: "gabcdefh", "v0.1" or "v0.1-5-gabcdefh".
+    """
+    # Get the repository
+    with open_repo_closing(repo) as r:
+        # Get a list of all tags
+        refs = r.get_refs()
+        tags = {}
+        for key, value in refs.items():
+            key = key.decode()
+            obj = r.get_object(value)
+            if u'tags' not in key:
+                continue
+
+            _, tag = key.rsplit(u'/', 1)
+
+            try:
+                commit = obj.object
+            except AttributeError:
+                continue
+            else:
+                commit = r.get_object(commit[1])
+            tags[tag] = [
+                datetime.datetime(*time.gmtime(commit.commit_time)[:6]),
+                commit.id.decode('ascii'),
+            ]
+
+        sorted_tags = sorted(tags.items(),
+                             key=lambda tag: tag[1][0],
+                             reverse=True)
+
+        # If there are no tags, return the current commit
+        if len(sorted_tags) == 0:
+            return 'g{}'.format(r[r.head()].id.decode('ascii')[:7])
+
+        # We're now 0 commits from the top
+        commit_count = 0
+
+        # Get the latest commit
+        latest_commit = r[r.head()]
+
+        # Walk through all commits
+        walker = r.get_walker()
+        for entry in walker:
+            # Check if tag
+            commit_id = entry.commit.id.decode('ascii')
+            for tag in sorted_tags:
+                tag_name = tag[0]
+                tag_commit = tag[1][1]
+                if commit_id == tag_commit:
+                    if commit_count == 0:
+                        return tag_name
+                    else:
+                        return '{}-{}-g{}'.format(
+                                tag_name,
+                                commit_count,
+                                latest_commit.id.decode('ascii')[:7])
+
+            commit_count += 1
+
+        # Return plain commit if no parent tag can be found
+        return 'g{}'.format(latest_commit.id.decode('ascii')[:7])

+ 25 - 2
dulwich/refs.py

@@ -772,18 +772,41 @@ class DiskRefsContainer(RefsContainer):
                     orig_ref = self.get_packed_refs().get(name, ZERO_SHA)
                 if orig_ref != old_ref:
                     return False
-            # may only be packed
+
+            # remove the reference file itself
             try:
                 os.remove(filename)
             except OSError as e:
-                if e.errno != errno.ENOENT:
+                if e.errno != errno.ENOENT:  # may only be packed
                     raise
+
             self._remove_packed_ref(name)
             self._log(name, old_ref, None, committer=committer,
                       timestamp=timestamp, timezone=timezone, message=message)
         finally:
             # never write, we just wanted the lock
             f.abort()
+
+        # outside of the lock, clean-up any parent directory that might now
+        # be empty. this ensures that re-creating a reference of the same
+        # name of what was previously a directory works as expected
+        parent = name
+        while True:
+            try:
+                parent, _ = parent.rsplit(b'/', 1)
+            except ValueError:
+                break
+
+            parent_filename = self.refpath(parent)
+            try:
+                os.rmdir(parent_filename)
+            except OSError as e:
+                # this can be caused by the parent directory being
+                # removed by another process, being not empty, etc.
+                # in any case, this is non fatal because we already
+                # removed the reference, just ignore it
+                break
+
         return True
 
 

+ 56 - 0
dulwich/tests/test_porcelain.py

@@ -1375,3 +1375,59 @@ class FsckTests(PorcelainTestCase):
         self.assertEqual(
                 [(obj.id, 'invalid name .git')],
                 [(sha, str(e)) for (sha, e) in porcelain.fsck(self.repo)])
+
+
+class DescribeTests(PorcelainTestCase):
+
+    def test_no_commits(self):
+        self.assertRaises(KeyError, porcelain.describe, self.repo.path)
+
+    def test_single_commit(self):
+        fullpath = os.path.join(self.repo.path, 'foo')
+        with open(fullpath, 'w') as f:
+            f.write("BAR")
+        porcelain.add(repo=self.repo.path, paths=[fullpath])
+        sha = porcelain.commit(
+                self.repo.path, message=b"Some message",
+                author=b"Joe <joe@example.com>",
+                committer=b"Bob <bob@example.com>")
+        self.assertEqual(
+                'g{}'.format(sha[:7].decode('ascii')),
+                porcelain.describe(self.repo.path))
+
+    def test_tag(self):
+        fullpath = os.path.join(self.repo.path, 'foo')
+        with open(fullpath, 'w') as f:
+            f.write("BAR")
+        porcelain.add(repo=self.repo.path, paths=[fullpath])
+        porcelain.commit(
+                self.repo.path, message=b"Some message",
+                author=b"Joe <joe@example.com>",
+                committer=b"Bob <bob@example.com>")
+        porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
+                             b'bar', annotated=True)
+        self.assertEqual(
+                "tryme",
+                porcelain.describe(self.repo.path))
+
+    def test_tag_and_commit(self):
+        fullpath = os.path.join(self.repo.path, 'foo')
+        with open(fullpath, 'w') as f:
+            f.write("BAR")
+        porcelain.add(repo=self.repo.path, paths=[fullpath])
+        porcelain.commit(
+                self.repo.path, message=b"Some message",
+                author=b"Joe <joe@example.com>",
+                committer=b"Bob <bob@example.com>")
+        porcelain.tag_create(self.repo.path, b"tryme", b'foo <foo@bar.com>',
+                             b'bar', annotated=True)
+        with open(fullpath, 'w') as f:
+            f.write("BAR2")
+        porcelain.add(repo=self.repo.path, paths=[fullpath])
+        sha = porcelain.commit(
+                self.repo.path, message=b"Some message",
+                author=b"Joe <joe@example.com>",
+                committer=b"Bob <bob@example.com>")
+        self.assertEqual(
+                'tryme-1-g{}'.format(sha[:7].decode('ascii')),
+                porcelain.describe(self.repo.path))

+ 17 - 0
dulwich/tests/test_refs.py

@@ -462,6 +462,23 @@ class DiskRefsContainerTests(RefsContainerTests, TestCase):
                 b'df6800012397fb85c56e7418dd4eb9405dee075c'))
         self.assertRaises(KeyError, lambda: self._refs[b'refs/tags/refs-0.1'])
 
+    def test_remove_parent(self):
+        self._refs[b'refs/heads/foo/bar'] = (
+            b'df6800012397fb85c56e7418dd4eb9405dee075c'
+        )
+        del self._refs[b'refs/heads/foo/bar']
+        ref_file = os.path.join(
+            self._refs.path, b'refs', b'heads', b'foo', b'bar',
+        )
+        self.assertFalse(os.path.exists(ref_file))
+        ref_file = os.path.join(self._refs.path, b'refs', b'heads', b'foo')
+        self.assertFalse(os.path.exists(ref_file))
+        ref_file = os.path.join(self._refs.path, b'refs', b'heads')
+        self.assertTrue(os.path.exists(ref_file))
+        self._refs[b'refs/heads/foo'] = (
+            b'df6800012397fb85c56e7418dd4eb9405dee075c'
+        )
+
     def test_read_ref(self):
         self.assertEqual(b'ref: refs/heads/master',
                          self._refs.read_ref(b'HEAD'))

+ 0 - 1
requirements.txt

@@ -1 +0,0 @@
-urllib3[secure]==1.22

+ 1 - 1
setup.py

@@ -14,7 +14,7 @@ from distutils.core import Distribution
 import os
 import sys
 
-dulwich_version_string = '0.19.4'
+dulwich_version_string = '0.19.5'
 
 include_dirs = []
 # Windows MSVC support