summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2021-02-17 12:30:36 +0800
committerGitHub <noreply@github.com>2021-02-17 12:30:36 +0800
commita880c5f4e00ef7bdfa3d55a187b6bb9c4fdd59ce (patch)
tree29ae8aba585cad35926f7cf812418e2d83b9bfba
parente1cd58ba862cce9cd9293933acd70b1a12feb5a8 (diff)
parent36811a2edc410589b5fde4d47d8d3a8a69d995ae (diff)
downloadgitpython-a880c5f4e00ef7bdfa3d55a187b6bb9c4fdd59ce.tar.gz
Merge pull request #1124 from larsks/feature/replace
add replace method to git.Commit
-rw-r--r--git/objects/commit.py43
-rw-r--r--test/test_commit.py20
2 files changed, 56 insertions, 7 deletions
diff --git a/git/objects/commit.py b/git/objects/commit.py
index 302798cb..45e6d772 100644
--- a/git/objects/commit.py
+++ b/git/objects/commit.py
@@ -136,6 +136,41 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable):
def _get_intermediate_items(cls, commit):
return commit.parents
+ @classmethod
+ def _calculate_sha_(cls, repo, commit):
+ '''Calculate the sha of a commit.
+
+ :param repo: Repo object the commit should be part of
+ :param commit: Commit object for which to generate the sha
+ '''
+
+ 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):
+ '''Create new commit object from existing commit object.
+
+ Any values provided as keyword arguments will replace the
+ corresponding attribute in the new object.
+ '''
+
+ attrs = {k: getattr(self, k) for k in self.__slots__}
+
+ for attrname in kwargs:
+ if attrname not in self.__slots__:
+ raise ValueError('invalid attribute name')
+
+ attrs.update(kwargs)
+ new_commit = self.__class__(self.repo, self.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
@@ -375,13 +410,7 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable):
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
diff --git a/test/test_commit.py b/test/test_commit.py
index 7260a233..2fe80530 100644
--- a/test/test_commit.py
+++ b/test/test_commit.py
@@ -101,6 +101,26 @@ class TestCommit(TestCommitSerialization):
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_replace_invalid_attribute(self):
+ commit = self.rorepo.commit('2454ae89983a4496a445ce347d7a41c0bb0ea7ae')
+
+ with self.assertRaises(ValueError):
+ commit.replace(badattr='This will never work')
+
def test_stats(self):
commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781')
stats = commit.stats