Пример #1
0
 def test_sorting(self):
     self.assertEqual(
         sorted(
             [Contributor('Jonathan Bedard'),
              Contributor('Aakash Jain')]),
         [Contributor('Aakash Jain'),
          Contributor('Jonathan Bedard')],
     )
Пример #2
0
    def test_branch_commits(self):
        with OutputCapture() as captured, mocks.local.Git(
                self.path) as mock, mocks.local.Svn(), MockTime:
            contirbutors = Contributor.Mapping()
            contirbutors.create('Jonathan Bedard', '*****@*****.**')

            local.Git(self.path).checkout('branch-a')
            mock.commits['branch-a'].append(
                Commit(
                    hash='f93138e3bf1d5ecca25fc0844b7a2a78b8e00aae',
                    branch='branch-a',
                    author=Contributor('*****@*****.**',
                                       emails=['*****@*****.**']),
                    branch_point=mock.commits['branch-a'][-1].branch_point,
                    identifier=mock.commits['branch-a'][-1].identifier + 1,
                    timestamp=1601668000,
                    message='New commit 1\n',
                ))
            mock.commits['branch-a'].append(
                Commit(
                    hash='0148c0df0faf248aa133d6d5ad911d7cb1b56a5b',
                    branch='branch-a',
                    author=Contributor('*****@*****.**',
                                       emails=['*****@*****.**']),
                    branch_point=mock.commits['branch-a'][-1].branch_point,
                    identifier=mock.commits['branch-a'][-1].identifier + 1,
                    timestamp=1601669000,
                    message='New commit 2\n',
                ))

            self.assertEqual(
                0,
                program.main(
                    args=('canonicalize', ),
                    path=self.path,
                    contributors=contirbutors,
                ))

            commit_a = local.Git(self.path).commit(branch='branch-a~1')
            self.assertEqual(commit_a.author,
                             contirbutors['*****@*****.**'])
            self.assertEqual(commit_a.message,
                             'New commit 1\nIdentifier: 2.3@branch-a')

            commit_b = local.Git(self.path).commit(branch='branch-a')
            self.assertEqual(commit_b.author,
                             contirbutors['*****@*****.**'])
            self.assertEqual(commit_b.message,
                             'New commit 2\nIdentifier: 2.4@branch-a')

        self.assertEqual(
            captured.stdout.getvalue(),
            'Rewrite f93138e3bf1d5ecca25fc0844b7a2a78b8e00aae (1/2) (--- seconds passed, remaining --- predicted)\n'
            'Rewrite 0148c0df0faf248aa133d6d5ad911d7cb1b56a5b (2/2) (--- seconds passed, remaining --- predicted)\n'
            '2 commits successfully canonicalized!\n',
        )
Пример #3
0
    def test_author_mapping(self):
        contributors = Contributor.Mapping()
        Contributor.from_scm_log('Author: Jonathan Bedard <*****@*****.**>',
                                 contributors)
        contributor = Contributor.from_scm_log(
            'Author: Jonathan Bedard <*****@*****.**>', contributors)

        self.assertEqual(contributor.name, 'Jonathan Bedard')
        self.assertEqual(contributor.emails,
                         ['*****@*****.**', '*****@*****.**'])
Пример #4
0
    def test_email_mapping(self):
        contributors = Contributor.Mapping()
        Contributor.from_scm_log('Author: Jonathan Bedard <*****@*****.**>',
                                 contributors)
        contributor = Contributor.from_scm_log(
            'r266751 | [email protected] | 2020-09-08 14:33:42 -0700 (Tue, 08 Sep 2020) | 10 lines',
            contributors)

        self.assertEqual(contributor.name, 'Jonathan Bedard')
        self.assertEqual(contributor.emails, ['*****@*****.**'])
Пример #5
0
    def test_hash(self):
        self.assertEqual(
            hash(Contributor('Jonathan Bedard', ['*****@*****.**'])),
            hash(Contributor('Jonathan Bedard', ['*****@*****.**'])),
        )

        self.assertNotEqual(
            hash(Contributor('Jonathan Bedard', ['*****@*****.**'])),
            hash(Contributor('Aakash Jain', ['*****@*****.**'])),
        )
Пример #6
0
    def test_string_comparison(self):
        self.assertEqual(
            Contributor('Jonathan Bedard', ['*****@*****.**']),
            'Jonathan Bedard',
        )

        self.assertNotEqual(
            Contributor('Jonathan Bedard', ['*****@*****.**']),
            'Aakash Jain',
        )
Пример #7
0
    def test_json_decode(self):
        contributor_a = Contributor(
            name='Jonathan Bedard',
            emails=['*****@*****.**', '*****@*****.**'],
        )

        dictionary = json.loads(
            json.dumps(contributor_a, cls=Contributor.Encoder))
        contributor_b = Contributor(**dictionary)

        self.assertEqual(contributor_a, contributor_b)
Пример #8
0
    def test_contributor(self):
        contributor = Contributor.from_scm_log(
            'Author: Jonathan Bedard <*****@*****.**>')

        commit = Commit(revision=1, identifier=1, author=contributor)
        self.assertEqual(commit.author, contributor)

        commit = Commit(revision=1,
                        identifier=1,
                        author=Contributor.Encoder().default(contributor))
        self.assertEqual(commit.author, contributor)
Пример #9
0
    def test_json_decode(self):
        contributor = Contributor.from_scm_log(
            'Author: Jonathan Bedard <*****@*****.**>')

        commit_a = Commit(revision=1,
                          hash='c3bd784f8b88bd03f64467ddd3304ed8be28acbe',
                          identifier='1@main',
                          timestamp=1000,
                          author=Contributor.Encoder().default(contributor),
                          message='Message')

        dictionary = json.loads(json.dumps(commit_a, cls=Commit.Encoder))
        commit_b = Commit(**dictionary)

        self.assertEqual(commit_a, commit_b)
Пример #10
0
    def _args_from_content(self, content, include_log=True):
        author = None
        timestamp = None

        for line in content.splitlines()[:4]:
            split = line.split(': ')
            if split[0] == 'Author':
                author = Contributor.from_scm_log(line.lstrip(),
                                                  self.contributors)
            elif split[0] == 'CommitDate':
                tz_diff = line.split(' ')[-1]
                date = datetime.strptime(split[1].lstrip()[:-len(tz_diff)],
                                         '%a %b %d %H:%M:%S %Y ')
                date += timedelta(
                    hours=int(tz_diff[1:3]),
                    minutes=int(tz_diff[3:5]),
                ) * (1 if tz_diff[0] == '-' else -1)
                timestamp = int(calendar.timegm(
                    date.timetuple())) - time.timezone

        message = ''
        for line in content.splitlines()[5:]:
            message += line[4:] + '\n'
        matches = self.GIT_SVN_REVISION.findall(message)

        return dict(
            revision=int(matches[-1].split('@')[0]) if matches else None,
            author=author,
            timestamp=timestamp,
            message=message.rstrip() if include_log else None,
        )
Пример #11
0
 def test_upper_case_email(self):
     mapping = Contributor.Mapping()
     mapping.create('Fujii Hironori', '*****@*****.**')
     self.assertEqual(mapping['*****@*****.**'].name,
                      'Fujii Hironori')
     self.assertEqual(mapping['*****@*****.**'].name,
                      'Fujii Hironori')
Пример #12
0
    def test_json_encode(self):
        contributor = Contributor.from_scm_log(
            'Author: Jonathan Bedard <*****@*****.**>')

        self.assertDictEqual(
            dict(
                revision=1,
                hash='c3bd784f8b88bd03f64467ddd3304ed8be28acbe',
                branch='main',
                identifier='1@main',
                timestamp=1000,
                order=0,
                author=dict(
                    name='Jonathan Bedard',
                    emails=['*****@*****.**'],
                ),
                message='Message',
            ),
            json.loads(
                json.dumps(Commit(
                    revision=1,
                    hash='c3bd784f8b88bd03f64467ddd3304ed8be28acbe',
                    identifier='1@main',
                    timestamp=1000,
                    author=contributor,
                    message='Message',
                ),
                           cls=Commit.Encoder)))
Пример #13
0
    def test_number(self):
        with OutputCapture() as captured, mocks.local.Git(
                self.path) as mock, mocks.local.Svn(), MockTime:
            contirbutors = Contributor.Mapping()
            contirbutors.create('Jonathan Bedard', '*****@*****.**')

            self.assertEqual(
                0,
                program.main(
                    args=('canonicalize', '--number', '3'),
                    path=self.path,
                    contributors=contirbutors,
                ))

            self.assertEqual(
                local.Git(self.path).commit(identifier='5@main').message,
                'Patch Series\nIdentifier: 5@main')
            self.assertEqual(
                local.Git(self.path).commit(identifier='4@main').message,
                '8th commit\nIdentifier: 4@main')
            self.assertEqual(
                local.Git(self.path).commit(identifier='3@main').message,
                '4th commit\nIdentifier: 3@main')

        self.assertEqual(
            captured.stdout.getvalue(),
            'Rewrite 1abe25b443e985f93b90d830e4a7e3731336af4d (1/3) (--- seconds passed, remaining --- predicted)\n'
            'Rewrite bae5d1e90999d4f916a8a15810ccfa43f37a2fd6 (2/3) (--- seconds passed, remaining --- predicted)\n'
            'Rewrite d8bce26fa65c6fc8f39c17927abb77f69fab82fc (3/3) (--- seconds passed, remaining --- predicted)\n'
            '3 commits successfully canonicalized!\n',
        )
Пример #14
0
    def test_short_svn_log(self):
        contributor = Contributor.from_scm_log(
            'r266751 | [email protected] | 2020-09-08 14:33:42 -0700 (Tue, 08 Sep 2020) | 1 line'
        )

        self.assertEqual(contributor.name, '*****@*****.**')
        self.assertEqual(contributor.emails, ['*****@*****.**'])
Пример #15
0
    def test_git_svn_log(self):
        contributor = Contributor.from_scm_log(
            'Author: Jonathan Bedard <[email protected]@268f45cc-cd09-0410-ab3c-d52691b4dbfc>'
        )

        self.assertEqual(contributor.name, 'Jonathan Bedard')
        self.assertEqual(contributor.emails, ['*****@*****.**'])
Пример #16
0
    def test_formated_identifier(self):
        with OutputCapture() as captured, mocks.local.Git(
                self.path) as mock, mocks.local.Svn(), MockTime:
            contirbutors = Contributor.Mapping()
            contirbutors.create('\u017dan Dober\u0161ek',
                                '*****@*****.**')

            mock.commits[mock.default_branch].append(
                Commit(
                    hash='38ea50d28ae394c9c8b80e13c3fb21f1c262871f',
                    branch=mock.default_branch,
                    author=Contributor('\u017dan Dober\u0161ek',
                                       emails=['*****@*****.**']),
                    identifier=mock.commits[mock.default_branch][-1].identifier
                    + 1,
                    timestamp=1601668000,
                    message='New commit\n',
                ))

            self.assertEqual(
                0,
                program.main(
                    args=(
                        'canonicalize',
                        '-v',
                    ),
                    path=self.path,
                    contributors=contirbutors,
                    identifier_template=
                    'Canonical link: https://commits.webkit.org/{}',
                ))

            commit = local.Git(self.path).commit(branch=mock.default_branch)
            self.assertEqual(commit.author,
                             contirbutors['*****@*****.**'])
            self.assertEqual(
                commit.message,
                'New commit\nCanonical link: https://commits.webkit.org/5@main'
            )

        self.assertEqual(
            captured.stdout.getvalue(),
            'Rewrite 38ea50d28ae394c9c8b80e13c3fb21f1c262871f (1/1) (--- seconds passed, remaining --- predicted)\n'
            'Overwriting 38ea50d28ae394c9c8b80e13c3fb21f1c262871f\n'
            '1 commit successfully canonicalized!\n',
        )
Пример #17
0
    def test_git_svn(self):
        with OutputCapture() as captured, mocks.local.Git(
                self.path, git_svn=True) as mock, mocks.local.Svn(), MockTime:
            contirbutors = Contributor.Mapping()
            contirbutors.create('Jonathan Bedard', '*****@*****.**')

            mock.commits[mock.default_branch].append(
                Commit(
                    hash='766609276fe201e7ce2c69994e113d979d2148ac',
                    branch=mock.default_branch,
                    author=Contributor('*****@*****.**',
                                       emails=['*****@*****.**']),
                    identifier=mock.commits[mock.default_branch][-1].identifier
                    + 1,
                    timestamp=1601668000,
                    revision=9,
                    message='New commit\n',
                ))

            self.assertEqual(
                0,
                program.main(
                    args=('canonicalize', '-vv'),
                    path=self.path,
                    contributors=contirbutors,
                ))

            commit = local.Git(self.path).commit(branch=mock.default_branch)
            self.assertEqual(commit.author, contirbutors['*****@*****.**'])
            self.assertEqual(
                commit.message,
                'New commit\n'
                'Identifier: 5@main\n'
                'svn-id: https://svn.example.org/repository/repository/trunk@9 268f45cc-cd09-0410-ab3c-d52691b4dbfc',
            )

        self.assertEqual(
            captured.stdout.getvalue(),
            'Rewrite 766609276fe201e7ce2c69994e113d979d2148ac (1/1) (--- seconds passed, remaining --- predicted)\n'
            'Overwriting 766609276fe201e7ce2c69994e113d979d2148ac\n'
            '    GIT_AUTHOR_NAME=Jonathan Bedard\n'
            '    [email protected]\n'
            '    GIT_COMMITTER_NAME=Jonathan Bedard\n'
            '    [email protected]\n'
            '1 commit successfully canonicalized!\n',
        )
Пример #18
0
 def __init__(self,
              dev_branches=None,
              prod_branches=None,
              contributors=None):
     self.dev_branches = dev_branches or self.DEV_BRANCHES
     self.prod_branches = prod_branches or self.PROD_BRANCHES
     self.path = None
     self.contributors = Contributor.Mapping(
     ) if contributors is None else contributors
Пример #19
0
    def __init__(self, dev_branches=None, prod_branches=None, contributors=None, id=None):
        self.dev_branches = dev_branches or self.DEV_BRANCHES
        self.prod_branches = prod_branches or self.PROD_BRANCHES
        self.path = None
        self.contributors = Contributor.Mapping() if contributors is None else contributors

        if id and not isinstance(id, six.string_types):
            raise ValueError("Expected 'id' to be a string type, not '{}'".format(type(id)))
        self.id = id
Пример #20
0
 def test_unicode(self):
     self.assertEqual(
         str(
             Contributor(u'Michael Br\u00fcning',
                         ['*****@*****.**'])),
         string_utils.encode(
             u'Michael Br\u00fcning <*****@*****.**>',
             target_type=str),
     )
Пример #21
0
    def test_from_json_str(self):
        contributor = Contributor.from_scm_log(
            'Author: [email protected] <*****@*****.**>')
        self.assertEqual(
            Commit.from_json('''{
    "revision": 1,
    "hash": "c3bd784f8b88bd03f64467ddd3304ed8be28acbe",
    "identifier": "1@main",
    "timestamp": 1000,
    "author": "*****@*****.**",
    "message": "Message"
}'''),
            Commit(revision=1,
                   hash='c3bd784f8b88bd03f64467ddd3304ed8be28acbe',
                   identifier='1@main',
                   timestamp=1000,
                   author=Contributor.Encoder().default(contributor),
                   message='Message'),
        )
Пример #22
0
    def test_existing_identifier(self):
        with OutputCapture() as captured, mocks.local.Git(
                self.path) as mock, mocks.local.Svn(), MockTime:
            contirbutors = Contributor.Mapping()
            contirbutors.create('Jonathan Bedard', '*****@*****.**')

            mock.commits[mock.default_branch].append(
                Commit(
                    hash='38ea50d28ae394c9c8b80e13c3fb21f1c262871f',
                    branch=mock.default_branch,
                    author=Contributor('Jonathan Bedard',
                                       emails=['*****@*****.**']),
                    identifier=mock.commits[mock.default_branch][-1].identifier
                    + 1,
                    timestamp=1601668000,
                    message='New commit\nIdentifier: {}@{}'.format(
                        mock.commits[mock.default_branch][-1].identifier + 1,
                        mock.default_branch,
                    ),
                ))

            self.assertEqual(
                0,
                program.main(
                    args=(
                        'canonicalize',
                        '-v',
                    ),
                    path=self.path,
                    contributors=contirbutors,
                ))

            commit = local.Git(self.path).commit(branch=mock.default_branch)
            self.assertEqual(commit.author, contirbutors['*****@*****.**'])
            self.assertEqual(commit.message, 'New commit\nIdentifier: 5@main')

        self.assertEqual(
            captured.stdout.getvalue(),
            'Rewrite 38ea50d28ae394c9c8b80e13c3fb21f1c262871f (1/1) (--- seconds passed, remaining --- predicted)\n'
            'Overwriting 38ea50d28ae394c9c8b80e13c3fb21f1c262871f\n'
            '1 commit successfully canonicalized!\n',
        )
Пример #23
0
 def test_json_encode(self):
     self.assertDictEqual(
         dict(
             name='Jonathan Bedard',
             emails=['*****@*****.**', '*****@*****.**'],
         ),
         json.loads(
             json.dumps(Contributor(
                 name='Jonathan Bedard',
                 emails=['*****@*****.**', '*****@*****.**'],
             ),
                        cls=Contributor.Encoder)))
Пример #24
0
    def test_saving(self):
        mapping_a = Contributor.Mapping()
        mapping_a.create('Jonathan Bedard', '*****@*****.**')
        mapping_a.create('Stephanie Lewis', '*****@*****.**')
        mapping_a['JonWBedard'] = mapping_a['*****@*****.**']

        file = StringIO()
        mapping_a.save(file)
        file.seek(0)

        mapping_b = Contributor.Mapping.load(file)
        self.assertEqual(mapping_a['*****@*****.**'],
                         mapping_b['*****@*****.**'])
        self.assertEqual(mapping_a['*****@*****.**'],
                         mapping_b['JonWBedard'])
        self.assertEqual(mapping_a['*****@*****.**'],
                         mapping_b['*****@*****.**'])
Пример #25
0
        def default(self, obj):
            if not isinstance(obj, Commit):
                return super(Commit.Encoder, self).default(obj)

            result = dict()
            for attribute in [
                    'hash', 'revision', 'branch', 'timestamp', 'message'
            ]:
                value = getattr(obj, attribute, None)
                if value is not None:
                    result[attribute] = value

            if obj.author:
                result['author'] = Contributor.Encoder().default(obj.author)

            if obj.identifier is not None:
                result['identifier'] = str(obj)

            return result
Пример #26
0
    def _args_from_content(self, content, include_log=True):
        leading = content.splitlines()[0]
        match = Contributor.SVN_AUTHOR_RE.match(
            leading) or Contributor.SVN_AUTHOR_Q_RE.match(leading)
        if not match:
            return {}

        tz_diff = match.group('date').split(' ', 2)[-1]
        date = datetime.strptime(
            match.group('date')[:-len(tz_diff)], '%Y-%m-%d %H:%M:%S ')
        date += timedelta(
            hours=int(tz_diff[1:3]),
            minutes=int(tz_diff[3:5]),
        ) * (1 if tz_diff[0] == '-' else -1)

        return dict(
            revision=int(match.group('revision')),
            timestamp=int(calendar.timegm(date.timetuple())),
            author=Contributor.from_scm_log(leading, self.contributors),
            message='\n'.join(content.splitlines()[2:]).rstrip()
            if include_log else None,
        )
Пример #27
0
        def default(self, obj):
            if isinstance(obj, dict):
                return {key: self.default(value) for key, value in obj.items()}
            if isinstance(obj, list):
                return [self.default(value) for value in obj]
            if not isinstance(obj, Commit):
                return super(Commit.Encoder, self).default(obj)

            result = dict()
            for attribute in [
                    'hash', 'revision', 'branch', 'timestamp', 'order',
                    'message', 'repository_id'
            ]:
                value = getattr(obj, attribute, None)
                if value is not None:
                    result[attribute] = value

            if obj.author:
                result['author'] = Contributor.Encoder().default(obj.author)

            if obj.identifier is not None:
                result['identifier'] = str(obj)

            return result
Пример #28
0
    def commit(self,
               hash=None,
               revision=None,
               identifier=None,
               branch=None,
               tag=None,
               include_log=True,
               include_identifier=True):
        # Only git-svn checkouts can convert revisions to fully qualified commits
        if revision and not self.is_svn:
            raise self.Exception(
                'This git checkout does not support SVN revisions')

        # Determine the hash for a provided Subversion revision
        elif revision:
            if hash:
                raise ValueError('Cannot define both hash and revision')

            revision = Commit._parse_revision(revision, do_assert=True)
            revision_log = run(
                [self.executable(), 'svn', 'find-rev', 'r{}'.format(revision)],
                cwd=self.root_path,
                capture_output=True,
                encoding='utf-8',
                timeout=3,
            )
            if revision_log.returncode:
                raise self.Exception(
                    "Failed to retrieve commit information for 'r{}'".format(
                        revision))
            hash = revision_log.stdout.rstrip()
            if not hash:
                raise self.Exception("Failed to find 'r{}'".format(revision))

        default_branch = self.default_branch
        parsed_branch_point = None
        log_format = ['-1'] if include_log else ['-1', '--format=short']

        # Determine the `git log` output and branch for a given identifier
        if identifier is not None:
            if revision:
                raise ValueError('Cannot define both revision and identifier')
            if hash:
                raise ValueError('Cannot define both hash and identifier')
            if tag:
                raise ValueError('Cannot define both tag and identifier')

            parsed_branch_point, identifier, parsed_branch = Commit._parse_identifier(
                identifier, do_assert=True)
            if parsed_branch:
                if branch and branch != parsed_branch:
                    raise ValueError(
                        "Caller passed both 'branch' and 'identifier', but specified different branches ({} and {})"
                        .format(
                            branch,
                            parsed_branch,
                        ), )
                branch = parsed_branch

            baseline = branch or 'HEAD'
            is_default = baseline == default_branch
            if baseline == 'HEAD':
                is_default = default_branch in self._branches_for(baseline)

            if is_default and parsed_branch_point:
                raise self.Exception(
                    'Cannot provide a branch point for a commit on the default branch'
                )

            base_count = self._commit_count(
                baseline if is_default else '{}..{}'.
                format(default_branch, baseline))

            if identifier > base_count:
                raise self.Exception(
                    'Identifier {} cannot be found on the specified branch in the current checkout'
                    .format(identifier))
            log = run(
                [
                    self.executable(), 'log', '{}~{}'.format(
                        branch or 'HEAD', base_count - identifier)
                ] + log_format,
                cwd=self.root_path,
                capture_output=True,
                encoding='utf-8',
            )
            if log.returncode:
                raise self.Exception(
                    "Failed to retrieve commit information for 'i{}@{}'".
                    format(identifier, branch or 'HEAD'))

            # Negative identifiers are actually commits on the default branch, we will need to re-compute the identifier
            if identifier < 0 and is_default:
                raise self.Exception(
                    'Illegal negative identifier on the default branch')
            if identifier < 0:
                identifier = None

        # Determine the `git log` output for a given branch or tag
        elif branch or tag:
            if hash:
                raise ValueError('Cannot define both tag/branch and hash')
            if branch and tag:
                raise ValueError('Cannot define both tag and branch')

            log = run([self.executable(), 'log', branch or tag] + log_format,
                      cwd=self.root_path,
                      capture_output=True,
                      encoding='utf-8')
            if log.returncode:
                raise self.Exception(
                    "Failed to retrieve commit information for '{}'".format(
                        branch or tag))

        # Determine the `git log` output for a given hash
        else:
            hash = Commit._parse_hash(hash, do_assert=True)
            log = run([self.executable(), 'log', hash or 'HEAD'] + log_format,
                      cwd=self.root_path,
                      capture_output=True,
                      encoding='utf-8')
            if log.returncode:
                raise self.Exception(
                    "Failed to retrieve commit information for '{}'".format(
                        hash or 'HEAD'))

        # Fully define the hash from the `git log` output
        match = self.GIT_COMMIT.match(log.stdout.splitlines()[0])
        if not match:
            raise self.Exception('Invalid commit hash in git log')
        hash = match.group('hash')

        # A commit is often on multiple branches, the canonical branch is the one with the highest priority
        branch = self.prioritize_branches(self._branches_for(hash))

        # Compute the identifier if the function did not receive one and we were asked to
        if not identifier and include_identifier:
            identifier = self._commit_count(
                hash if branch ==
                default_branch else '{}..{}'.format(default_branch, hash))

        # Only compute the branch point we're on something other than the default branch
        branch_point = None if not include_identifier or branch == default_branch else self._commit_count(
            hash) - identifier
        if branch_point and parsed_branch_point and branch_point != parsed_branch_point:
            raise ValueError(
                "Provided 'branch_point' does not match branch point of specified branch"
            )

        # Check the commit log for a git-svn revision
        logcontent = '\n'.join(line[4:]
                               for line in log.stdout.splitlines()[4:])
        matches = self.GIT_SVN_REVISION.findall(logcontent)
        revision = int(matches[-1].split('@')[0]) if matches else None

        # We only care about when a commit was commited
        commit_time = run(
            [self.executable(), 'show', '-s', '--format=%ct', hash],
            cwd=self.root_path,
            capture_output=True,
            encoding='utf-8',
        )
        if commit_time.returncode:
            raise self.Exception(
                'Failed to retrieve commit time for {}'.format(hash))
        timestamp = int(commit_time.stdout.lstrip())

        # Comparing commits in different repositories involves comparing timestamps. This is problematic because it git,
        # it's possible for a series of commits to share a commit time. To handle this case, we assign each commit a
        # zero-indexed "order" within it's timestamp.
        order = 0
        while not identifier or order + 1 < identifier + (branch_point or 0):
            commit_time = run(
                [
                    self.executable(), 'show', '-s', '--format=%ct',
                    '{}~{}'.format(hash, order + 1)
                ],
                cwd=self.root_path,
                capture_output=True,
                encoding='utf-8',
            )
            if commit_time.returncode:
                break
            if int(commit_time.stdout.lstrip()) != timestamp:
                break
            order += 1

        return Commit(
            repository_id=self.id,
            hash=hash,
            revision=revision,
            identifier=identifier if include_identifier else None,
            branch_point=branch_point,
            branch=branch,
            timestamp=timestamp,
            order=order,
            author=Contributor.from_scm_log(log.stdout.splitlines()[1],
                                            self.contributors),
            message=logcontent if include_log else None,
        )
Пример #29
0
    def __init__(
        self,
        hash=None,
        revision=None,
        identifier=None,
        branch=None,
        branch_point=None,
        timestamp=None,
        author=None,
        message=None,
    ):
        self.hash = self._parse_hash(hash, do_assert=True)
        self.revision = self._parse_revision(revision, do_assert=True)

        parsed_identifier = self._parse_identifier(identifier, do_assert=True)
        if parsed_identifier:
            parsed_branch_point, self.identifier, parsed_branch = parsed_identifier
            self.branch_point = parsed_branch_point or branch_point
            self.branch = parsed_branch or branch
        else:
            self.identifier = None
            self.branch_point = branch_point
            self.branch = branch

        if branch and not isinstance(branch, six.string_types):
            raise ValueError("Expected 'branch' to be a string")
        if branch and branch != self.branch:
            raise ValueError(
                "Caller passed both 'branch' and 'identifier', but specified different branches ({} and {})"
                .format(
                    branch,
                    self.branch,
                ), )

        if branch_point and not isinstance(branch_point, int):
            raise ValueError("Expected 'branch_point' to be an int")
        if branch_point and branch_point != self.branch_point:
            raise ValueError(
                "Caller passed both 'branch_point' and 'identifier', but specified different values ({} and {})"
                .format(
                    branch_point,
                    self.branch_point,
                ), )

        if timestamp and not isinstance(timestamp, int):
            raise TypeError(
                "Expected 'timestamp' to be of type int, got '{}'".format(
                    timestamp))
        self.timestamp = timestamp

        if author and isinstance(author, dict) and author.get('name'):
            self.author = Contributor(author.get('name'), author.get('emails'))
        elif author and not isinstance(author, Contributor):
            raise TypeError(
                "Expected 'author' to be of type {}, got '{}'".format(
                    Contributor, author))
        else:
            self.author = author

        if message and not isinstance(message, six.string_types):
            raise ValueError(
                "Expected 'message' to be a string, got '{}'".format(message))
        self.message = message

        # Force a commit format check
        self.__repr__()
Пример #30
0
    def filter_branch(self,
                      range,
                      identifier_template=None,
                      environment_shell=None):
        # We can't effectively mock the bash script in the command, but we can mock the python code that
        # script calls, which is where the program logic is.
        head, start = range.split('...')
        head = self.find(head)
        start = self.find(start)

        commits_to_edit = []
        for commit in reversed(self.commits[head.branch]):
            if commit.branch == start.branch and commit.identifier <= start.identifier:
                break
            commits_to_edit.insert(0, commit)
        if head.branch != self.default_branch:
            for commit in reversed(
                    self.commits[self.default_branch][:head.branch_point]):
                if commit.identifier <= start.identifier:
                    break
                commits_to_edit.insert(0, commit)

        stdout = StringIO()
        original_env = {
            key: os.environ.get('OLDPWD')
            for key in [
                'OLDPWD',
                'GIT_COMMIT',
                'GIT_AUTHOR_NAME',
                'GIT_AUTHOR_EMAIL',
                'GIT_COMMITTER_NAME',
                'GIT_COMMITTER_EMAIL',
            ]
        }

        try:
            count = 0
            os.environ['OLDPWD'] = self.path
            for commit in commits_to_edit:
                count += 1
                os.environ['GIT_COMMIT'] = commit.hash
                os.environ['GIT_AUTHOR_NAME'] = commit.author.name
                os.environ['GIT_AUTHOR_EMAIL'] = commit.author.email
                os.environ['GIT_COMMITTER_NAME'] = commit.author.name
                os.environ['GIT_COMMITTER_EMAIL'] = commit.author.email

                stdout.write(
                    'Rewrite {hash} ({count}/{total}) (--- seconds passed, remaining --- predicted)\n'
                    .format(
                        hash=commit.hash,
                        count=count,
                        total=len(commits_to_edit),
                    ))

                if identifier_template:
                    messagefile = StringIO()
                    messagefile.write(commit.message)
                    messagefile.seek(0)
                    with OutputCapture() as captured:
                        message_main(messagefile, identifier_template)
                    lines = captured.stdout.getvalue().splitlines()
                    if lines[-1].startswith('git-svn-id: https://svn'):
                        lines.pop(-1)
                    commit.message = '\n'.join(lines)

                if not environment_shell:
                    continue
                if re.search(r'echo "Overwriting', environment_shell):
                    stdout.write('Overwriting {}\n'.format(commit.hash))

                match = re.search(r'(?P<json>\S+\.json)', environment_shell)
                if match:
                    with OutputCapture() as captured:
                        committer_main(match.group('json'))
                    captured.stdout.seek(0)
                    for line in captured.stdout.readlines():
                        line = line.rstrip()
                        os.environ[line.split(' ')[0]] = ' '.join(
                            line.split(' ')[1:])

                commit.author = Contributor(
                    name=os.environ['GIT_AUTHOR_NAME'],
                    emails=[os.environ['GIT_AUTHOR_EMAIL']])

                if re.search(r'echo "\s+', environment_shell):
                    for key in [
                            'GIT_AUTHOR_NAME', 'GIT_AUTHOR_EMAIL',
                            'GIT_COMMITTER_NAME', 'GIT_COMMITTER_EMAIL'
                    ]:
                        stdout.write('    {}={}\n'.format(
                            key, os.environ[key]))

        finally:
            for key, value in original_env.items():
                if value is not None:
                    os.environ[key] = value
                else:
                    del os.environ[key]

        return mocks.ProcessCompletion(
            returncode=0,
            stdout=stdout.getvalue(),
        )