def git_repo(self): with mock.patch("publish.Repo", spec=Repo, spec_set=True) as mock_repo: commits = [ Commit(mock_repo, _h2b("111111"), message="First commit", parents=tuple()), Commit(mock_repo, _h2b("222222"), message="Second commit", parents=("111111",)), Commit(mock_repo, _h2b("333333"), message="Third commit", parents=("222222",)) ] mock_repo.iter_commits.return_value = commits rev_parse_returns = { "heads/master": commits[-1], PREVIOUS_TAG: TagObject(mock_repo, _h2b("aaaaaa"), object=commits[-2], tag=PREVIOUS_TAG), CURRENT_TAG: TagObject(mock_repo, _h2b("bbbbbb"), object=commits[-1], tag=CURRENT_TAG) } mock_repo.rev_parse.side_effect = lambda x: rev_parse_returns[x] mock_repo.git.rev_parse.side_effect = lambda x, **kwargs: x def describe(rev=None, **kwargs): print("call to describe(%r, %r)" % (rev, kwargs)) if rev is None: return CURRENT_TAG if rev.endswith("^"): if rev.startswith(CURRENT_TAG): return PREVIOUS_TAG raise GitCommandError("describe", "failed") raise AssertionError("Test wants to describe something unexpected: rev=%r, kwargs=%r" % (rev, kwargs)) mock_repo.git.describe.side_effect = describe yield mock_repo
def test_serialization_unicode_support(self): assert Commit.default_encoding.lower() == 'utf-8' # create a commit with unicode in the message, and the author's name # Verify its serialization and deserialization cmt = self.rorepo.commit('0.1.6') assert isinstance(cmt.message, text_type) # it automatically decodes it as such assert isinstance(cmt.author.name, text_type) # same here cmt.message = u"üäêèß" assert len(cmt.message) == 5 cmt.author.name = u"äüß" assert len(cmt.author.name) == 3 cstream = BytesIO() cmt._serialize(cstream) cstream.seek(0) assert len(cstream.getvalue()) ncmt = Commit(self.rorepo, cmt.binsha) ncmt._deserialize(cstream) assert cmt.author.name == ncmt.author.name assert cmt.message == ncmt.message # actually, it can't be printed in a shell as repr wants to have ascii only # it appears cmt.author.__repr__()
def archive(test_viewer: TestViewer) -> Experiment: """ 将某次 test 对应 commit 的文件打包,相关命令为 git archive -o <filename> <commit-id> :param test_viewer: :return: """ repo = test_viewer.repo commit = Commit(repo, hex_to_bin(test_viewer.json_info['commit_hash'])) old_path = os.getcwd() os.chdir(commit.tree.abspath) exp = Experiment('Archive') revert_path = exp.makedir('archive') revert_fn = os.path.join(revert_path, "file.zip") exp.regist_plugin('archive', { 'file': revert_fn, 'test_name': test_viewer.test_name }) with open(revert_fn, 'wb') as w: commit.repo.archive(w, commit) exp.end() os.chdir(old_path) return exp
def assert_gpgsig_deserialization(self, cstream): assert 'gpgsig' in 'precondition: need gpgsig' class RepoMock: def __init__(self, bytestr): self.bytestr = bytestr @property def odb(self): class ODBMock: def __init__(self, bytestr): self.bytestr = bytestr def stream(self, *args): stream = Mock(spec_set=['read'], return_value=self.bytestr) stream.read.return_value = self.bytestr return ('binsha', 'typename', 'size', stream) return ODBMock(self.bytestr) repo_mock = RepoMock(cstream.getvalue()) for field in Commit.__slots__: c = Commit(repo_mock, b'x' * 20) assert getattr(c, field) is not None
def git_commit(): time = "Sat, 13 Dec 2019 12:40:00 +0000" repo = Repo(path) os.environ["GIT_AUTHOR_DATE"] = time os.environ["GIT_COMMITTER_DATE"] = time repo.git.add('--all') comm = Commit(repo, Commit.NULL_BIN_SHA, None, None, time, 0, None, time, 0, COMMIT_MESSAGE, None, None) #for x in range(0, 100000): #try: comm.message = 'noonce' + COMMIT_MESSAGE print(comm.hexsha)
def assert_commit_serialization(self, rwrepo, commit_id, print_performance_info=False): """traverse all commits in the history of commit identified by commit_id and check if the serialization works. :param print_performance_info: if True, we will show how fast we are""" ns = 0 # num serializations nds = 0 # num deserializations st = time.time() for cm in rwrepo.commit(commit_id).traverse(): nds += 1 # assert that we deserialize commits correctly, hence we get the same # sha on serialization stream = BytesIO() cm._serialize(stream) ns += 1 streamlen = stream.tell() stream.seek(0) istream = rwrepo.odb.store(IStream(Commit.type, streamlen, stream)) self.assertEqual(istream.hexsha, cm.hexsha.encode('ascii')) nc = Commit(rwrepo, Commit.NULL_BIN_SHA, cm.tree, cm.author, cm.authored_date, cm.author_tz_offset, cm.committer, cm.committed_date, cm.committer_tz_offset, cm.message, cm.parents, cm.encoding) self.assertEqual(nc.parents, cm.parents) stream = BytesIO() nc._serialize(stream) ns += 1 streamlen = stream.tell() stream.seek(0) # reuse istream istream.size = streamlen istream.stream = stream istream.binsha = None nc.binsha = rwrepo.odb.store(istream).binsha # if it worked, we have exactly the same contents ! self.assertEqual(nc.hexsha, cm.hexsha) # END check commits elapsed = time.time() - st if print_performance_info: print( "Serialized %i and deserialized %i commits in %f s ( (%f, %f) commits / s" % (ns, nds, elapsed, ns / elapsed, nds / elapsed), file=sys.stderr)
def reset(test_viewer: TestViewer) -> Experiment: """ 将工作目录中的文件恢复到某个commit 恢复快照的 git 流程: git branch experiment git add . & git commit -m ... // 保证文件最新,防止冲突报错,这一步由 Experiment() 代为完成 git checkout <commit-id> // 恢复文件到 <commit-id> git checkout -b reset // 将当前状态附到新的临时分支 reset 上 git branch experiment // 切换回 experiment 分支 git add . & git commit -m ... // 将当前状态重新提交到最新 // 此时experiment 中最新的commit 为恢复的<commit-id> git branch -D reset // 删除临时分支 git branch master // 最终回到原来分支,保证除文件变动外git状态完好 :param test_viewer: TestViewer :return: """ repo = test_viewer.repo commit = Commit(repo, hex_to_bin(test_viewer.json_info['commit_hash'])) old_path = os.getcwd() os.chdir(commit.tree.abspath) exp = Experiment('Reset') repo = commit.repo # type:Repo with branch(commit.repo, _GITKEY.thexp_branch) as new_branch: repo.git.checkout(commit.hexsha) repo.git.checkout('-b', 'reset') repo.head.reference = new_branch repo.git.add('.') ncommit = repo.index.commit("Reset from {}".format(commit.hexsha)) repo.git.branch('-d', 'reset') exp.regist_plugin( 'reset', { 'test_name': test_viewer.test_name, # 从哪个状态恢复 'from': exp.commit.hexsha, # reset 运行时的快照 'where': commit.hexsha, # 恢复到哪一次 commit,是恢复前的保存的状态 'to': ncommit.hexsha, # 对恢复后的状态再次进行提交,此时 from 和 to 两次提交状态应该完全相同 }) exp.end() os.chdir(old_path) return exp
def commit(request, commit_hexsha): binsha = binascii.unhexlify(commit_hexsha) commit = Commit(repo, binsha) details = { 'hexsha': commit.hexsha, 'author': commit.author.name, 'message': commit.message, 'authored_datetime': commit.authored_datetime, 'files': [{ 'filename': key, 'changes': value } for key, value in commit.stats.files.items()], 'author_email': commit.author.email } return JsonResponse({'commit': details})
def test_commit_serialization(self): self.assert_commit_serialization(self.gitrwrepo, '58c78e6', True) rwrepo = self.gitrwrepo make_object = rwrepo.odb.store # direct serialization - deserialization can be tested afterwards # serialization is probably limited on IO hc = rwrepo.commit(rwrepo.head) nc = 5000 st = time() for i in range(nc): cm = Commit(rwrepo, Commit.NULL_BIN_SHA, hc.tree, hc.author, hc.authored_date, hc.author_tz_offset, hc.committer, hc.committed_date, hc.committer_tz_offset, str(i), parents=hc.parents, encoding=hc.encoding) stream = BytesIO() cm._serialize(stream) slen = stream.tell() stream.seek(0) cm.binsha = make_object(IStream(Commit.type, slen, stream)).binsha # END commit creation elapsed = time() - st print( "Serialized %i commits to loose objects in %f s ( %f commits / s )" % (nc, elapsed, nc / elapsed), file=sys.stderr)
def __setstate__(self, state: dict): from gitdb.util import hex_to_bin from git import Commit self._start_time = state['_start_time'] self._time_fmt = state['_time_fmt'] self._exp_name = state['_exp_name'] self._exp_dir = state['_exp_dir'] self._test_dir = state['_test_dir'] self._project_dir = state['_project_dir'] self._hold_dirs = state['_hold_dirs'] self._plugins = state['_plugins'] self._tags = state['_tags'] self._exc_dict = state.get('_exc_dict', None) self._end_state = state['_end_state'] self._in_main = state['_in_main'] self._repo = None self._commit = Commit(self.repo, hex_to_bin(state['_commit'])) self._config = globs
def GetCommitsBetweenIds(newer: str, older: str) -> list: """ Get a list of commits between two Git commits. Args: newer (str): The newer Git commit id for the comparison. older (str): The older Git commit id for the comparison. Returns: A list of commits. """ commits = list() output = subprocess.check_output([ 'git', 'log', '{newer}...{older}'.format(newer=newer, older=older) ]).decode('utf-8') if (len(output) < 1): return commits commitLog = str(output).split('\n') lineIndex = 0 while (lineIndex < len(commitLog)): commitLogLine = str(commitLog[lineIndex]) if (commitLogLine.startswith('commit')): # Commit log item startsexpression commit = Commit() # Get commit hash from the commit commit.hash = str(commitLog[lineIndex]).split(' ')[1] lineIndex = lineIndex + 1 # Get "Author:", "{author's name} <{author's email}>" logLine = str(commitLog[lineIndex]).split(':', 1)[1].lstrip() user = User() user.name = logLine.split('<', 1)[0].rstrip(' ') user.email = logLine.split('<', 1)[1].lstrip().replace( '\r', '').replace('\n', '') commit.author = user lineIndex = lineIndex + 1 # Split "Date: ", "{actual date}" logLine = str(commitLog[lineIndex]).split('Date:')[1] dateString = logLine = logLine.lstrip().replace('\r', '').replace( '\n', '') commit.date = Date.ConvertGitStringToDate(dateString) # Skip extra line between date and commit title lineIndex = lineIndex + 2 # Add the title to the commit but remove leading whitespace and trailing linefeed commit.title = str(commitLog[lineIndex]).lstrip().replace( '\r', '').replace('\n', '') lineIndex = lineIndex + 1 # Skip extra line between title and commit message lineIndex = lineIndex + 1 # Commit message lasts until another commit message starts or the log ends commit.message = "" while (lineIndex < len(commitLog)): if (str(commitLog[lineIndex]).startswith('commit')): break commit.message = commit.message + commitLog[ lineIndex].lstrip() lineIndex = lineIndex + 1 commits.append(commit) break else: lineIndex = lineIndex + 1 return commits
def test_equality(self): commit1 = Commit(self.rorepo, Commit.NULL_BIN_SHA) commit2 = Commit(self.rorepo, Commit.NULL_BIN_SHA) commit3 = Commit(self.rorepo, "\1" * 20) assert_equal(commit1, commit2) assert_not_equal(commit2, commit3)
def test_repr(self): commit = Commit(self.rorepo, Commit.NULL_BIN_SHA) assert_equal('<git.Commit "%s">' % Commit.NULL_HEX_SHA, repr(commit))
def test_str(self): commit = Commit(self.rorepo, Commit.NULL_BIN_SHA) assert_equal(Commit.NULL_HEX_SHA, str(commit))
def get_commit(self, head: bytes) -> Commit: return Commit(self._get_repo(), head)
def get_commit_files(git_repo, commit_sha): return Commit(git_repo, commit_sha).stats.files
def create_commits(cls, ids: List[str], messages: List[str]) -> List[Commit]: return [ Commit(repo=Mock(), binsha=commit, message=message) for commit, message in zip(ids, messages) ]
def build_commits(cls, commits: List[str], messages: List[str]) -> List[Commit]: return [ Commit(repo=Mock(), binsha=commit, message=message) for commit, message in zip(commits, messages) ]
def commit(self): if self._commit is None: from gitdb.util import hex_to_bin from git import Commit self._commit = Commit(self.repo, hex_to_bin(self.commit_hash)) return self._commit