def test_sorting(self): self.assertEqual( sorted( [Contributor('Jonathan Bedard'), Contributor('Aakash Jain')]), [Contributor('Aakash Jain'), Contributor('Jonathan Bedard')], )
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', )
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, ['*****@*****.**', '*****@*****.**'])
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, ['*****@*****.**'])
def test_hash(self): self.assertEqual( hash(Contributor('Jonathan Bedard', ['*****@*****.**'])), hash(Contributor('Jonathan Bedard', ['*****@*****.**'])), ) self.assertNotEqual( hash(Contributor('Jonathan Bedard', ['*****@*****.**'])), hash(Contributor('Aakash Jain', ['*****@*****.**'])), )
def test_string_comparison(self): self.assertEqual( Contributor('Jonathan Bedard', ['*****@*****.**']), 'Jonathan Bedard', ) self.assertNotEqual( Contributor('Jonathan Bedard', ['*****@*****.**']), 'Aakash Jain', )
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)
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)
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)
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, )
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')
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)))
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', )
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, ['*****@*****.**'])
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, ['*****@*****.**'])
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', )
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', )
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
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
def test_unicode(self): self.assertEqual( str( Contributor(u'Michael Br\u00fcning', ['*****@*****.**'])), string_utils.encode( u'Michael Br\u00fcning <*****@*****.**>', target_type=str), )
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'), )
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', )
def test_json_encode(self): self.assertDictEqual( dict( name='Jonathan Bedard', emails=['*****@*****.**', '*****@*****.**'], ), json.loads( json.dumps(Contributor( name='Jonathan Bedard', emails=['*****@*****.**', '*****@*****.**'], ), cls=Contributor.Encoder)))
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['*****@*****.**'])
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
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, )
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
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, )
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__()
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(), )