Skip to content

Commit

Permalink
add replace method to git.Commit
Browse files Browse the repository at this point in the history
This adds a replace method to git.Commit. The replace method returns a
copy of the Commit object with attributes replaced from keyword
arguments. For example:

>>> old = repo.head.commit
>>> new = old.replace(message='This is a test')

closes #1123
  • Loading branch information
larsks committed Feb 16, 2021
1 parent e1cd58b commit 826cf16
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 7 deletions.
26 changes: 19 additions & 7 deletions git/objects/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut
def _get_intermediate_items(cls, commit):
return commit.parents

@classmethod
def _calculate_sha_(cls, repo, commit):
stream = BytesIO()
commit._serialize(stream)
streamlen = stream.tell()
stream.seek(0)

istream = repo.odb.store(IStream(cls.type, streamlen, stream))
return istream.binsha

def replace(self, **kwargs):
attrs = {k: getattr(self, k) for k in self.__slots__}
attrs.update(kwargs)
new_commit = self.__class__(self.repo, self.__class__.NULL_BIN_SHA, **attrs)
new_commit.binsha = self._calculate_sha_(self.repo, new_commit)

return new_commit

def _set_cache_(self, attr):
if attr in Commit.__slots__:
# read the data in a chunk, its faster - then provide a file wrapper
Expand Down Expand Up @@ -375,13 +393,7 @@ def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False,
committer, committer_time, committer_offset,
message, parent_commits, conf_encoding)

stream = BytesIO()
new_commit._serialize(stream)
streamlen = stream.tell()
stream.seek(0)

istream = repo.odb.store(IStream(cls.type, streamlen, stream))
new_commit.binsha = istream.binsha
new_commit.binsha = cls._calculate_sha_(repo, new_commit)

if head:
# need late import here, importing git at the very beginning throws
Expand Down
14 changes: 14 additions & 0 deletions test/test_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ def test_bake(self):
assert isinstance(commit.author_tz_offset, int) and isinstance(commit.committer_tz_offset, int)
self.assertEqual(commit.message, "Added missing information to docstrings of commit and stats module\n")

def test_replace_no_changes(self):
old_commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
new_commit = old_commit.replace()

for attr in old_commit.__slots__:
assert getattr(new_commit, attr) == getattr(old_commit, attr)

def test_replace_new_sha(self):
commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
new_commit = commit.replace(message='Added replace method')

assert new_commit.hexsha == 'fc84cbecac1bd4ba4deaac07c1044889edd536e6'
assert new_commit.message == 'Added replace method'

def test_stats(self):
commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781')
stats = commit.stats
Expand Down

0 comments on commit 826cf16

Please sign in to comment.