Skip to content

Commit 826cf16

Browse files
committed
add replace method to git.Commit
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 gitpython-developers#1123
1 parent e1cd58b commit 826cf16

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

git/objects/commit.py

+19-7
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,24 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut
136136
def _get_intermediate_items(cls, commit):
137137
return commit.parents
138138

139+
@classmethod
140+
def _calculate_sha_(cls, repo, commit):
141+
stream = BytesIO()
142+
commit._serialize(stream)
143+
streamlen = stream.tell()
144+
stream.seek(0)
145+
146+
istream = repo.odb.store(IStream(cls.type, streamlen, stream))
147+
return istream.binsha
148+
149+
def replace(self, **kwargs):
150+
attrs = {k: getattr(self, k) for k in self.__slots__}
151+
attrs.update(kwargs)
152+
new_commit = self.__class__(self.repo, self.__class__.NULL_BIN_SHA, **attrs)
153+
new_commit.binsha = self._calculate_sha_(self.repo, new_commit)
154+
155+
return new_commit
156+
139157
def _set_cache_(self, attr):
140158
if attr in Commit.__slots__:
141159
# read the data in a chunk, its faster - then provide a file wrapper
@@ -375,13 +393,7 @@ def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False,
375393
committer, committer_time, committer_offset,
376394
message, parent_commits, conf_encoding)
377395

378-
stream = BytesIO()
379-
new_commit._serialize(stream)
380-
streamlen = stream.tell()
381-
stream.seek(0)
382-
383-
istream = repo.odb.store(IStream(cls.type, streamlen, stream))
384-
new_commit.binsha = istream.binsha
396+
new_commit.binsha = cls._calculate_sha_(repo, new_commit)
385397

386398
if head:
387399
# need late import here, importing git at the very beginning throws

test/test_commit.py

+14
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ def test_bake(self):
101101
assert isinstance(commit.author_tz_offset, int) and isinstance(commit.committer_tz_offset, int)
102102
self.assertEqual(commit.message, "Added missing information to docstrings of commit and stats module\n")
103103

104+
def test_replace_no_changes(self):
105+
old_commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
106+
new_commit = old_commit.replace()
107+
108+
for attr in old_commit.__slots__:
109+
assert getattr(new_commit, attr) == getattr(old_commit, attr)
110+
111+
def test_replace_new_sha(self):
112+
commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
113+
new_commit = commit.replace(message='Added replace method')
114+
115+
assert new_commit.hexsha == 'fc84cbecac1bd4ba4deaac07c1044889edd536e6'
116+
assert new_commit.message == 'Added replace method'
117+
104118
def test_stats(self):
105119
commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781')
106120
stats = commit.stats

0 commit comments

Comments
 (0)