diff --git a/git/cmd.py b/git/cmd.py index 13c01401d..7757f05ca 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -147,6 +147,12 @@ def dict_to_slots_and__excluded_are_none(self, d, excluded=()): else 0) +class MissingObject(ValueError): + def __init__(self, sha1, *args, **kwargs): + super(MissingObject, self).__init__(*args, **kwargs) + self.sha1 = sha1 + + class Git(LazyMixin): """ @@ -1023,7 +1029,7 @@ def _parse_object_header(self, header_line): if not tokens: raise ValueError("SHA could not be resolved, git returned: %r" % (header_line.strip())) else: - raise ValueError("SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) + raise MissingObject(tokens[0], "SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) # END handle actual return value # END error handling diff --git a/git/objects/commit.py b/git/objects/commit.py index f29fbaa28..76e06107c 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -66,12 +66,12 @@ class Commit(base.Object, Iterable, Diffable, Traversable, Serializable): __slots__ = ("tree", "author", "authored_date", "author_tz_offset", "committer", "committed_date", "committer_tz_offset", - "message", "parents", "encoding", "gpgsig") + "message", "parents", "encoding", "gpgsig", "predecessors") _id_attribute_ = "hexsha" def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, author_tz_offset=None, committer=None, committed_date=None, committer_tz_offset=None, - message=None, parents=None, encoding=None, gpgsig=None): + message=None, parents=None, encoding=None, gpgsig=None, predecessors=None): """Instantiate a new Commit. All keyword arguments taking None as default will be implicitly set on first query. @@ -101,6 +101,9 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut :param parents: List or tuple of Commit objects which are our parent(s) in the commit dependency graph + :param predecessors: + List or tuple of Commit objects which this commit replaces after rebasing or amending + rewrite graph :return: git.Commit :note: @@ -132,6 +135,8 @@ def __init__(self, repo, binsha, tree=None, author=None, authored_date=None, aut self.encoding = encoding if gpgsig is not None: self.gpgsig = gpgsig + if predecessors is not None: + self.predecessors = predecessors @classmethod def _get_intermediate_items(cls, commit): @@ -280,7 +285,7 @@ def _iter_from_process_or_stream(cls, repo, proc_or_stream): @classmethod def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False, author=None, committer=None, - author_date=None, commit_date=None): + author_date=None, commit_date=None, predecessors=None): """Commit the given tree, creating a commit object. :param repo: Repo object the commit should be part of @@ -325,6 +330,15 @@ def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False, # end check parent commit types # END if parent commits are unset + if predecessors is None: + predecessors=list() + else: + for p in predecessors: + if not isinstance(p, cls): + raise ValueError("Replaced commit '%r' must be of type %s" % (p, cls)) + # end check predecessors commit types + # END if predecessors commits are unset + # retrieve all additional information, create a commit object, and # serialize it # Generally: @@ -375,7 +389,7 @@ def create_from_tree(cls, repo, tree, message, parent_commits=None, head=False, new_commit = cls(repo, cls.NULL_BIN_SHA, tree, author, author_time, author_offset, committer, committer_time, committer_offset, - message, parent_commits, conf_encoding) + message, parent_commits, conf_encoding, predecessors=predecessors) stream = BytesIO() new_commit._serialize(stream) @@ -434,6 +448,9 @@ def _serialize(self, stream): except AttributeError: pass + for p in self.predecessors: + write(("predecessor %s\n" % p).encode('ascii')) + write(b"\n") # write plain bytes, be sure its encoded according to our encoding @@ -480,6 +497,7 @@ def _deserialize(self, stream): self.gpgsig = None # read headers + self.predecessors = list() enc = next_line buf = enc.strip() while buf: @@ -501,6 +519,9 @@ def _deserialize(self, stream): self.gpgsig = sig.rstrip(b"\n").decode('ascii') if is_next_header: continue + elif buf[0:12] == b"predecessor ": + predecessor_sha = buf[buf.find(b' ') + 1:].decode('ascii') + self.predecessors.append(type(self)(self.repo, hex_to_bin(predecessor_sha))) buf = readline().strip() # decode the authors name