using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { /// /// A Commit /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public class Commit : GitObject { private readonly GitObjectLazyGroup group1; private readonly GitObjectLazyGroup group2; private readonly ILazy lazyTree; private readonly ILazy lazyAuthor; private readonly ILazy lazyCommitter; private readonly ILazy lazyMessage; private readonly ILazy lazyMessageShort; private readonly ILazy lazyEncoding; private readonly ParentsCollection parents; private readonly Lazy> lazyNotes; /// /// Needed for mocking purposes. /// protected Commit() { } internal Commit(Repository repo, ObjectId id) : base(repo, id) { lazyTree = GitObjectLazyGroup.Singleton(this.repo, id, obj => new Tree(this.repo, Proxy.git_commit_tree_id(obj), null)); group1 = new GitObjectLazyGroup(this.repo, id); lazyAuthor = group1.AddLazy(Proxy.git_commit_author); lazyCommitter = group1.AddLazy(Proxy.git_commit_committer); group2 = new GitObjectLazyGroup(this.repo, id); lazyMessage = group2.AddLazy(Proxy.git_commit_message); lazyMessageShort = group2.AddLazy(Proxy.git_commit_summary); lazyEncoding = group2.AddLazy(RetrieveEncodingOf); lazyNotes = new Lazy>(() => RetrieveNotesOfCommit(id).ToList()); parents = new ParentsCollection(repo, id); } /// /// Gets the pointed at by the in the . /// /// Path to the from the tree in this /// null if nothing has been found, the otherwise. public virtual TreeEntry this[string relativePath] { get { return Tree[relativePath]; } } /// /// Gets the commit message. /// public virtual string Message { get { return lazyMessage.Value; } } /// /// Gets the short commit message which is usually the first line of the commit. /// public virtual string MessageShort { get { return lazyMessageShort.Value; } } /// /// Gets the encoding of the message. /// public virtual string Encoding { get { return lazyEncoding.Value; } } /// /// Gets the author of this commit. /// public virtual Signature Author { get { return lazyAuthor.Value; } } /// /// Gets the committer. /// public virtual Signature Committer { get { return lazyCommitter.Value; } } /// /// Gets the Tree associated to this commit. /// public virtual Tree Tree { get { return lazyTree.Value; } } /// /// Gets the parents of this commit. This property is lazy loaded and can throw an exception if the commit no longer exists in the repo. /// public virtual IEnumerable Parents { get { return parents; } } /// /// Gets the notes of this commit. /// public virtual IEnumerable Notes { get { return lazyNotes.Value; } } private IEnumerable RetrieveNotesOfCommit(ObjectId oid) { return repo.Notes[oid]; } private static string RetrieveEncodingOf(ObjectHandle obj) { string encoding = Proxy.git_commit_message_encoding(obj); return encoding ?? "UTF-8"; } /// /// Prettify a commit message /// /// Remove comment lines and trailing lines /// /// /// The prettified message /// The message to prettify. /// Comment character. Lines starting with it will be removed public static string PrettifyMessage(string message, char commentChar) { return Proxy.git_message_prettify(message, commentChar); } private string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "{0} {1}", Id.ToString(7), MessageShort); } } /// /// Extract the signature data from this commit /// /// The signature and the signed data /// The repository in which the object lives /// The commit to extract the signature from /// The header field which contains the signature; use null for the default of "gpgsig" public static SignatureInfo ExtractSignature(Repository repo, ObjectId id, string field) { return Proxy.git_commit_extract_signature(repo.Handle, id, field); } /// /// Extract the signature data from this commit /// /// The overload uses the default header field "gpgsig" /// /// /// The signature and the signed data /// The repository in which the object lives /// The commit to extract the signature from public static SignatureInfo ExtractSignature(Repository repo, ObjectId id) { return Proxy.git_commit_extract_signature(repo.Handle, id, null); } /// /// Create a commit in-memory /// /// Prettifing the message includes: /// * Removing empty lines from the beginning and end. /// * Removing trailing spaces from every line. /// * Turning multiple consecutive empty lines between paragraphs into just one empty line. /// * Ensuring the commit message ends with a newline. /// * Removing every line starting with the . /// /// /// The of who made the change. /// The of who added the change to the repository. /// The description of why a change was made to the repository. /// The of the to be created. /// The parents of the to be created. /// True to prettify the message, or false to leave it as is. /// When non null, lines starting with this character will be stripped if prettifyMessage is true. /// The contents of the commit object. public static string CreateBuffer(Signature author, Signature committer, string message, Tree tree, IEnumerable parents, bool prettifyMessage, char? commentChar) { Ensure.ArgumentNotNull(message, "message"); Ensure.ArgumentDoesNotContainZeroByte(message, "message"); Ensure.ArgumentNotNull(author, "author"); Ensure.ArgumentNotNull(committer, "committer"); Ensure.ArgumentNotNull(tree, "tree"); Ensure.ArgumentNotNull(parents, "parents"); if (prettifyMessage) { message = Proxy.git_message_prettify(message, commentChar); } return Proxy.git_commit_create_buffer(tree.repo.Handle, author, committer, message, tree, parents.ToArray()); } private class ParentsCollection : ICollection { private readonly Lazy> _parents; private readonly Lazy _count; public ParentsCollection(Repository repo, ObjectId commitId) { _count = new Lazy(() => Proxy.git_commit_parentcount(repo.Handle, commitId)); _parents = new Lazy>(() => RetrieveParentsOfCommit(repo, commitId)); } private ICollection RetrieveParentsOfCommit(Repository repo, ObjectId commitId) { using (var obj = new ObjectSafeWrapper(commitId, repo.Handle)) { int parentsCount = _count.Value; var parents = new List(parentsCount); for (uint i = 0; i < parentsCount; i++) { ObjectId parentCommitId = Proxy.git_commit_parent_id(obj.ObjectPtr, i); parents.Add(new Commit(repo, parentCommitId)); } return parents; } } public IEnumerator GetEnumerator() { return _parents.Value.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(Commit item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(Commit item) { return _parents.Value.Contains(item); } public void CopyTo(Commit[] array, int arrayIndex) { _parents.Value.CopyTo(array, arrayIndex); } public bool Remove(Commit item) { throw new NotSupportedException(); } public int Count { get { return _count.Value; } } public bool IsReadOnly { get { return true; } } } } }