Title: [280436] trunk/Tools
Revision
280436
Author
jbed...@apple.com
Date
2021-07-29 11:32:23 -0700 (Thu, 29 Jul 2021)

Log Message

[git-webkit] Add identifiers to 'log' and 'blame'
https://bugs.webkit.org/show_bug.cgi?id=228027
<rdar://problem/80691164>

Reviewed by Dewei Zhu.

* Scripts/libraries/webkitscmpy/setup.py: Bump version.
* Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
* Scripts/libraries/webkitscmpy/webkitscmpy/local/scm.py:
(Scm.from_path): Pass all kwargs to local SCM object.
* Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py:
(main): Add Blame and Log commands.
* Scripts/libraries/webkitscmpy/webkitscmpy/program/blame.py: Added.
(Blame): Invoke pager.
* Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py:
(FilteredCommand): Shared command that handles replace hashes/revisions in
an SCM command with identifiers.
(FilteredCommand.parser): Allow user to change the prefered commit representation.
(FilteredCommand.pager): Pass output through more if called from a terminal.
(FilteredCommand.main): Modify output of provided command to replace commit representation
with the preffered commit representation.
* Scripts/libraries/webkitscmpy/webkitscmpy/program/log.py: Added.
(Log): Invoke pager.
* Scripts/libraries/webkitscmpy/webkitscmpy/test/log_unittest.py: Added.
(TestLog):
(TestLog.setUp):
(TestLog.test_git):
(TestLog.test_git_svn):
(TestLog.test_git):

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (280435 => 280436)


--- trunk/Tools/ChangeLog	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/ChangeLog	2021-07-29 18:32:23 UTC (rev 280436)
@@ -1,5 +1,37 @@
 2021-07-29  Jonathan Bedard  <jbed...@apple.com>
 
+        [git-webkit] Add identifiers to 'log' and 'blame'
+        https://bugs.webkit.org/show_bug.cgi?id=228027
+        <rdar://problem/80691164>
+
+        Reviewed by Dewei Zhu.
+
+        * Scripts/libraries/webkitscmpy/setup.py: Bump version.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/local/scm.py:
+        (Scm.from_path): Pass all kwargs to local SCM object.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py:
+        (main): Add Blame and Log commands.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/blame.py: Added.
+        (Blame): Invoke pager.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py:
+        (FilteredCommand): Shared command that handles replace hashes/revisions in
+        an SCM command with identifiers.
+        (FilteredCommand.parser): Allow user to change the prefered commit representation.
+        (FilteredCommand.pager): Pass output through more if called from a terminal.
+        (FilteredCommand.main): Modify output of provided command to replace commit representation
+        with the preffered commit representation.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/log.py: Added.
+        (Log): Invoke pager.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/test/log_unittest.py: Added.
+        (TestLog):
+        (TestLog.setUp):
+        (TestLog.test_git):
+        (TestLog.test_git_svn):
+        (TestLog.test_git):
+
+2021-07-29  Jonathan Bedard  <jbed...@apple.com>
+
         [webkitcorepy] Catch AttributeError when getting password
         https://bugs.webkit.org/show_bug.cgi?id=228590
         <rdar://problem/81278799>

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/setup.py (280435 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/setup.py	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/setup.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -29,7 +29,7 @@
 
 setup(
     name='webkitscmpy',
-    version='0.15.0',
+    version='1.0.0',
     description='Library designed to interact with git and svn repositories.',
     long_description=readme(),
     classifiers=[

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py (280435 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -46,7 +46,7 @@
         "Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
     )
 
-version = Version(0, 15, 0)
+version = Version(1, 0, 0)
 
 AutoInstall.register(Package('fasteners', Version(0, 15, 0)))
 AutoInstall.register(Package('monotonic', Version(1, 5)))

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/local/scm.py (280435 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/local/scm.py	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/local/scm.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -45,13 +45,13 @@
         return os.path.realpath(path)
 
     @classmethod
-    def from_path(cls, path, contributors=None):
+    def from_path(cls, path, contributors=None, **kwargs):
         from webkitscmpy import local
 
         if local.Git.is_checkout(path):
-            return local.Git(path, contributors=contributors)
+            return local.Git(path, contributors=contributors, **kwargs)
         if local.Svn.is_checkout(path):
-            return local.Svn(path, contributors=contributors)
+            return local.Svn(path, contributors=contributors, **kwargs)
         raise OSError("'{}' is not a known SCM type".format(path))
 
     def __init__(self, path, dev_branches=None, prod_branches=None, contributors=None, id=None):

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py (280435 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -132,6 +132,15 @@
                     begin=int(args[3].split(':')[-1]),
                 ) if self.connected else mocks.ProcessCompletion(returncode=1)
             ), mocks.Subprocess.Route(
+                self.executable, 'log',
+                cwd=self.path,
+                generator=lambda *args, **kwargs:
+                    self._log_range(
+                        branch='trunk',
+                        begin=self.commits['trunk'][0].revision,
+                        end=self.commits['trunk'][-1].revision,
+                    ) if self.connected else mocks.ProcessCompletion(returncode=1)
+            ), mocks.Subprocess.Route(
                 self.executable, 'up', '-r', re.compile(r'\d+'),
                 cwd=self.path,
                 generator=lambda *args, **kwargs:

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py (280435 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -24,18 +24,20 @@
 import logging
 import os
 
-from webkitcorepy import arguments, log as webkitcorepy_log
-from webkitscmpy import local, log, remote
-
+from .blame import Blame
 from .canonicalize import Canonicalize
 from .clean import Clean
 from .command import Command
 from .checkout import Checkout
 from .find import Find, Info
+from .log import Log
 from .pull import Pull
 from .setup_git_svn import SetupGitSvn
 
+from webkitcorepy import arguments, log as webkitcorepy_log
+from webkitscmpy import local, log, remote
 
+
 def main(args=None, path=None, loggers=None, contributors=None, identifier_template=None, subversion=None):
     logging.basicConfig(level=logging.WARNING)
 
@@ -61,7 +63,7 @@
 
     subparsers = parser.add_subparsers(help='sub-command help')
 
-    programs = [Find, Info, Checkout, Canonicalize, Pull, Clean]
+    programs = [Find, Info, Checkout, Canonicalize, Pull, Clean, Log, Blame]
     if subversion:
         programs.append(SetupGitSvn)
 

Copied: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/blame.py (from rev 280435, trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py) (0 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/blame.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/blame.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2021 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import sys
+
+from webkitscmpy import local
+from webkitscmpy.program.command import FilteredCommand
+
+
+class Blame(FilteredCommand):
+    name = 'blame'
+    help = "Filter raw output of 'git blame' or 'svn blame' to replace native commit representation with identifiers"
+
+    @classmethod
+    def main(cls, args, repository, **kwargs):
+        return cls.pager(args, repository, file=__file__, **kwargs)
+
+
+if __name__ == '__main__':
+    sys.exit(Blame.main(
+        sys.argv[3:],
+        local.Scm.from_path(path=sys.argv[1], cached=True),
+        representation=sys.argv[2],
+    ))

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py (280435 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py	2021-07-29 18:18:00 UTC (rev 280435)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -20,9 +20,16 @@
 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import os
+import re
+import subprocess
 import sys
+import time
 
+from webkitscmpy.commit import Commit
+from whichcraft import which
 
+
 class Command(object):
     name = None
     help = None
@@ -38,3 +45,204 @@
     def main(cls, args, repository, **kwargs):
         sys.stderr.write('No command specified\n')
         return -1
+
+
+class FilteredCommand(object):
+    IDENTIFIER = 'identifier'
+    HASH = 'hash'
+    REVISION = 'revision'
+
+    GIT_HEADER_RE = re.compile(r'^commit (?P<hash>[a-f0-9A-F]+)')
+    SVN_HEADER_RE = re.compile(r'^(?P<revision>r/d+) | ')
+
+    REVISION_RES = (re.compile(r'^(?P<revision>\d+)\s'), re.compile(r'(?P<revision>[rR]\d+)'))
+    HASH_RES = (re.compile(r'^(?P<hash>[a-f0-9A-F]{8}[a-f0-9A-F]+)\s'), re.compile(r'(?P<hash>[a-f0-9A-F]{8}[a-f0-9A-F]+)'))
+    IDENTIFIER_RES = (re.compile(r'^(?P<identifier>(\d+\.)?\d+@\S*)'), re.compile(r'(?P<identifier>(\d+\.)?\d+@\S*)'))
+    NO_FILTER_RES = [re.compile(r'    Canonical link:'), re.compile(r'    git-svn-id:')]
+
+    REPLACE_MODE = 0
+    APPEND_MODE = 1
+    HEADER_MODE = 2
+
+    @classmethod
+    def parser(cls, parser, loggers=None):
+        parser.add_argument(
+            'args', nargs='*',
+            type=str, default=None,
+            help='Arguments to be passed to tbe native source-code management tool',
+        )
+        parser.add_argument(
+            '--identifier', '-i',
+            help='Represent commits as identifiers',
+            dest='representation',
+            action='', const=cls.IDENTIFIER,
+            default=cls.IDENTIFIER,
+        )
+        parser.add_argument(
+            '--hash',
+            help='Represent commits as hashes',
+            dest='representation',
+            action='', const=cls.HASH,
+            default=cls.IDENTIFIER,
+        )
+        parser.add_argument(
+            '--revision', '-r',
+            help='Represent commits as revisions',
+            dest='representation',
+            action='', const=cls.REVISION,
+            default=cls.IDENTIFIER,
+        )
+        parser.add_argument(
+            '--representation', type=str,
+            help='Change method used to represent a commit (identifier/hash/revision)',
+            dest='representation',
+            default=cls.IDENTIFIER,
+        )
+
+    @classmethod
+    def pager(cls, args, repository, file=None, **kwargs):
+        if not repository.path:
+            sys.stderr.write("Cannot run '{}' on remote repository\n".format(cls.name))
+            return 1
+
+        if not getattr(repository, 'cache', None) and getattr(repository, 'Cache', None):
+            repository.cache = repository.Cache(repository)
+
+        # If we're a terminal, rely on 'more' to display output
+        if sys.stdin.isatty() and not isinstance(args, list) and file:
+            environ = os.environ
+            environ['PYTHONPATH'] = ':'.join(sys.path)
+
+            child = subprocess.Popen(
+                [sys.executable, file, repository.root_path, args.representation] + args.args,
+                env=environ,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+            )
+            more = subprocess.Popen([which('more'), '-F'], stdin=child.stdout)
+
+            try:
+                while more.poll() is None and not child.poll():
+                    time.sleep(0.25)
+            finally:
+                child.kill()
+                more.kill()
+                child_error = child.stderr.read()
+                if child_error:
+                    sys.stderr.buffer.write(b'\n' + child_error)
+
+            return child.returncode
+
+        return FilteredCommand.main(args, repository, command=cls.name, **kwargs)
+
+    @classmethod
+    def main(cls, args, repository, command=None, representation=None, **kwargs):
+        if not repository.path:
+            sys.stderr.write("Cannot run '{}' on remote repository\n".format(command))
+            return 1
+
+        cache = getattr(repository, 'cache', None)
+        if not cache:
+            sys.stderr.write('No cache available, cannot performantly map commit references\n')
+            return 1
+
+        if not isinstance(args, list):
+            representation = representation or args.representation
+            args = args.args
+        else:
+            representation = representation or cls.IDENTIFIER
+
+        if representation not in (cls.IDENTIFIER, cls.HASH, cls.REVISION):
+            sys.stderr.write("'{}' is not a valid commit representation\n".format(representation))
+            return 1
+
+        for index in range(len(args)):
+            parsed = Commit.parse(args[index], do_assert=False)
+            if not parsed:
+                continue
+            replacement = None
+            if repository.is_svn:
+                replacement = repository.cache.to_revision(hash=parsed.hash, identifier=str(parsed) if parsed.identifier else None)
+            if repository.is_git:
+                replacement = repository.cache.to_hash(revision=parsed.revision, identifier=str(parsed) if parsed.identifier else None)
+            if replacement:
+                args[index] = replacement
+
+        log_output = subprocess.Popen(
+            [repository.executable(), command] + args,
+            cwd=repository.root_path,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            **(dict(encoding='utf-8') if sys.version_info > (3, 0) else dict())
+        )
+        log_output.poll()
+
+        def replace_line(match, mode=cls.APPEND_MODE, **kwargs):
+            reference = kwargs.get(representation)
+            if not reference:
+                reference = getattr(cache, 'to_{}'.format(representation), lambda **kwargs: None)(**kwargs)
+
+            if reference:
+                if isinstance(reference, int):
+                    reference = 'r{}'.format(reference)
+                if representation == 'hash':
+                    reference = reference[:Commit.HASH_LABEL_SIZE]
+                if mode == cls.APPEND_MODE:
+                    reference = '{} ({})'.format(match.group(1), reference)
+                if mode == cls.HEADER_MODE:
+                    alternates = [] if match.group(1).startswith(reference) else [match.group(1)]
+                    for repr in {'revision', 'hash', 'identifier'} - {'hash' if repository.is_git else 'revision', representation}:
+                        if repr in kwargs:
+                            continue
+                        other = getattr(cache, 'to_{}'.format(repr), lambda **kwargs: None)(**kwargs)
+                        if not other:
+                            continue
+                        if other == 'hash':
+                            other = other[:Commit.HASH_LABEL_SIZE]
+                        alternates.append('r{}'.format(other) if isinstance(other, int) else other)
+                    reference = '{} ({})'.format(reference, ', '.join(alternates))
+                return match.group(0).replace(match.group(1), reference)
+            return match.group(0)
+
+        res = {}
+        if representation != cls.HASH:
+            res[cls.HASH] = cls.HASH_RES
+        if representation != cls.REVISION:
+            res[cls.REVISION] = cls.REVISION_RES
+        if representation != cls.IDENTIFIER:
+            res[cls.IDENTIFIER] = cls.IDENTIFIER_RES
+
+        header_re = cls.GIT_HEADER_RE if repository.is_git else cls.SVN_HEADER_RE
+
+        try:
+            line = log_output.stdout.readline()
+            while line:
+                header = header_re.sub(
+                    lambda match: replace_line(match, mode=cls.HEADER_MODE, **{'hash' if repository.is_git else 'revision': match.group(1)}),
+                    line,
+                )
+                if header != line:
+                    sys.stdout.write(header)
+                    line = log_output.stdout.readline()
+                    continue
+
+                for repr, regexs in res.items():
+                    line = regexs[0].sub(
+                        lambda match: replace_line(match, mode=cls.REPLACE_MODE, **{repr: match.group(1)}),
+                        line,
+                    )
+
+                if not any(r.match(line) for r in cls.NO_FILTER_RES):
+                    for repr, regexs in res.items():
+                        line = regexs[1].sub(
+                            lambda match: replace_line(match, mode=cls.APPEND_MODE, **{repr: match.group(1)}),
+                            line,
+                        )
+
+                sys.stdout.write(line)
+
+                line = log_output.stdout.readline()
+
+        finally:
+            log_output.kill()
+        return log_output.returncode

Copied: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/log.py (from rev 280435, trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/command.py) (0 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/log.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/log.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2021 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import sys
+
+from webkitscmpy import local
+from webkitscmpy.program.command import FilteredCommand
+
+
+class Log(FilteredCommand):
+    name = 'log'
+    help = "Filter raw output of 'git log' or 'svn log' to replace native commit representation with identifiers"
+
+    @classmethod
+    def main(cls, args, repository, **kwargs):
+        return cls.pager(args, repository, file=__file__, **kwargs)
+
+
+if __name__ == '__main__':
+    sys.exit(Log.main(
+        sys.argv[3:],
+        local.Scm.from_path(path=sys.argv[1], cached=True),
+        representation=sys.argv[2],
+    ))

Added: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/log_unittest.py (0 => 280436)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/log_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/log_unittest.py	2021-07-29 18:32:23 UTC (rev 280436)
@@ -0,0 +1,206 @@
+# Copyright (C) 2021 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import json
+import os
+import shutil
+import tempfile
+
+from datetime import datetime
+from webkitcorepy import OutputCapture, testing
+from webkitcorepy.mocks import Time as MockTime
+from webkitscmpy import program, mocks
+
+
+class TestLog(testing.PathTestCase):
+    basepath = 'mock/repository'
+
+    def setUp(self):
+        super(TestLog, self).setUp()
+        os.mkdir(os.path.join(self.path, '.git'))
+        os.mkdir(os.path.join(self.path, '.svn'))
+
+    def test_git(self):
+        with OutputCapture() as captured, mocks.local.Git(self.path), mocks.local.Svn(), MockTime:
+            self.assertEqual(-1, program.main(
+                args=('log', 'main'),
+                path=self.path,
+            ))
+
+        self.assertEqual(
+            captured.stdout.getvalue(),
+            '''commit 5@main (d8bce26fa65c6fc8f39c17927abb77f69fab82fc)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 03:46:40 2020 +0000
+
+    Patch Series
+
+commit 4@main (bae5d1e90999d4f916a8a15810ccfa43f37a2fd6)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 03:46:40 2020 +0000
+
+    8th commit
+
+commit 3@main (1abe25b443e985f93b90d830e4a7e3731336af4d)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 02:23:20 2020 +0000
+
+    4th commit
+
+commit 2@main (fff83bb2d9171b4d9196e977eb0508fd57e7a08d)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 01:50:00 2020 +0000
+
+    2nd commit
+
+commit 1@main (9b8311f25a77ba14923d9d5a6532103f54abefcb)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 01:33:20 2020 +0000
+
+    1st commit
+''',
+        )
+
+    def test_git_svn(self):
+        with OutputCapture() as captured, mocks.local.Git(self.path, git_svn=True), mocks.local.Svn(), MockTime:
+            self.assertEqual(-1, program.main(
+                args=('log', 'main'),
+                path=self.path,
+            ))
+
+            self.assertEqual(
+                captured.stdout.getvalue(),
+                '''commit 5@main (d8bce26fa65c6fc8f39c17927abb77f69fab82fc, r9)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 03:46:40 2020 +0000
+
+    Patch Series
+    git-svn-id: https://svn.example.org/repository/repository/trunk@9 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit 4@main (bae5d1e90999d4f916a8a15810ccfa43f37a2fd6, r8)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 03:46:40 2020 +0000
+
+    8th commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@8 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit 3@main (1abe25b443e985f93b90d830e4a7e3731336af4d, r4)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 02:23:20 2020 +0000
+
+    4th commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@4 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit 2@main (fff83bb2d9171b4d9196e977eb0508fd57e7a08d, r2)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 01:50:00 2020 +0000
+
+    2nd commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@2 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit 1@main (9b8311f25a77ba14923d9d5a6532103f54abefcb, r1)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 01:33:20 2020 +0000
+
+    1st commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@1 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+''',
+            )
+
+    def test_git_svn_revision(self):
+        with OutputCapture() as captured, mocks.local.Git(self.path, git_svn=True), mocks.local.Svn(), MockTime:
+            self.assertEqual(-1, program.main(
+                args=('log', 'main', '--revision'),
+                path=self.path,
+            ))
+
+            self.maxDiff = None
+            self.assertEqual(
+                captured.stdout.getvalue(),
+                '''commit r9 (d8bce26fa65c6fc8f39c17927abb77f69fab82fc, 5@main)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 03:46:40 2020 +0000
+
+    Patch Series
+    git-svn-id: https://svn.example.org/repository/repository/trunk@9 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit r8 (bae5d1e90999d4f916a8a15810ccfa43f37a2fd6, 4@main)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 03:46:40 2020 +0000
+
+    8th commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@8 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit r4 (1abe25b443e985f93b90d830e4a7e3731336af4d, 3@main)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 02:23:20 2020 +0000
+
+    4th commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@4 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit r2 (fff83bb2d9171b4d9196e977eb0508fd57e7a08d, 2@main)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 01:50:00 2020 +0000
+
+    2nd commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@2 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+commit r1 (9b8311f25a77ba14923d9d5a6532103f54abefcb, 1@main)
+Author: Jonathan Bedard <jbed...@apple.com>
+Date:   Sat Oct 03 01:33:20 2020 +0000
+
+    1st commit
+    git-svn-id: https://svn.example.org/repository/repository/trunk@1 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+''',
+            )
+
+    def test_svn(self):
+        with OutputCapture() as captured, mocks.local.Git(), mocks.local.Svn(self.path), MockTime:
+            self.assertEqual(-1, program.main(
+                args=('log',),
+                path=self.path,
+            ))
+
+        self.assertEqual(
+            captured.stdout.getvalue(),
+            '''------------------------------------------------------------------------
+r6 (4@trunk) | jbed...@apple.com | 2020-10-02 18:58:20 0000 (Fri, 02 Oct 2020) | 1 lines
+
+6th commit
+
+------------------------------------------------------------------------
+r4 (3@trunk) | jbed...@apple.com | 2020-10-02 18:25:00 0000 (Fri, 02 Oct 2020) | 1 lines
+
+4th commit
+
+------------------------------------------------------------------------
+r2 (2@trunk) | jbed...@apple.com | 2020-10-02 17:51:40 0000 (Fri, 02 Oct 2020) | 1 lines
+
+2nd commit
+
+------------------------------------------------------------------------
+r1 (1@trunk) | jbed...@apple.com | 2020-10-02 17:35:00 0000 (Fri, 02 Oct 2020) | 1 lines
+
+1st commit
+
+''',
+        )
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to