using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { /// /// Holds the result of a diff between two trees. /// Changes at the granularity of the file can be obtained through the different sub-collections , and . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public class TreeChanges : IEnumerable { private readonly IDictionary changes = new Dictionary(); private readonly List added = new List(); private readonly List deleted = new List(); private readonly List modified = new List(); private readonly List typeChanged = new List(); private readonly List unmodified = new List(); private int linesAdded; private int linesDeleted; private readonly IDictionary> fileDispatcher = Build(); private readonly StringBuilder fullPatchBuilder = new StringBuilder(); private static IDictionary> Build() { return new Dictionary> { { ChangeKind.Modified, (de, d) => de.modified.Add(d) }, { ChangeKind.Deleted, (de, d) => de.deleted.Add(d) }, { ChangeKind.Added, (de, d) => de.added.Add(d) }, { ChangeKind.TypeChanged, (de, d) => de.typeChanged.Add(d) }, { ChangeKind.Unmodified, (de, d) => de.unmodified.Add(d) }, }; } /// /// Needed for mocking purposes. /// protected TreeChanges() { } internal TreeChanges(DiffListSafeHandle diff) { Proxy.git_diff_foreach(diff, FileCallback, null, DataCallback); Proxy.git_diff_print_patch(diff, PrintCallBack); } private int DataCallback(GitDiffDelta delta, GitDiffRange range, GitDiffLineOrigin lineOrigin, IntPtr content, UIntPtr contentLen, IntPtr payload) { var filePath = FilePathMarshaler.FromNative(delta.NewFile.Path); AddLineChange(this[filePath], lineOrigin); return 0; } private int FileCallback(GitDiffDelta delta, float progress, IntPtr payload) { AddFileChange(delta); return 0; } private int PrintCallBack(GitDiffDelta delta, GitDiffRange range, GitDiffLineOrigin lineorigin, IntPtr content, UIntPtr contentlen, IntPtr payload) { string formattedoutput = Utf8Marshaler.FromNative(content, (int)contentlen); var filePath = FilePathMarshaler.FromNative(delta.NewFile.Path); fullPatchBuilder.Append(formattedoutput); this[filePath].AppendToPatch(formattedoutput); return 0; } private void AddLineChange(Changes currentChange, GitDiffLineOrigin lineOrigin) { switch (lineOrigin) { case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION: linesAdded++; currentChange.LinesAdded++; break; case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION: linesDeleted++; currentChange.LinesDeleted++; break; } } private void AddFileChange(GitDiffDelta delta) { var newFilePath = FilePathMarshaler.FromNative(delta.NewFile.Path); var oldFilePath = FilePathMarshaler.FromNative(delta.OldFile.Path); var newMode = (Mode)delta.NewFile.Mode; var oldMode = (Mode)delta.OldFile.Mode; var newOid = delta.NewFile.Oid; var oldOid = delta.OldFile.Oid; if (delta.Status == ChangeKind.Untracked || delta.Status == ChangeKind.Ignored) { delta.Status = ChangeKind.Added; } var diffFile = new TreeEntryChanges(newFilePath, newMode, newOid, delta.Status, oldFilePath, oldMode, oldOid, delta.IsBinary()); fileDispatcher[delta.Status](this, diffFile); changes.Add(newFilePath, diffFile); } #region IEnumerable Members /// /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. public virtual IEnumerator GetEnumerator() { return changes.Values.GetEnumerator(); } /// /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion /// /// Gets the corresponding to the specified . /// public virtual TreeEntryChanges this[string path] { get { return this[(FilePath)path]; } } private TreeEntryChanges this[FilePath path] { get { TreeEntryChanges treeEntryChanges; if (changes.TryGetValue(path, out treeEntryChanges)) { return treeEntryChanges; } return null; } } /// /// List of that have been been added. /// public virtual IEnumerable Added { get { return added; } } /// /// List of that have been deleted. /// public virtual IEnumerable Deleted { get { return deleted; } } /// /// List of that have been modified. /// public virtual IEnumerable Modified { get { return modified; } } /// /// List of which type have been changed. /// public virtual IEnumerable TypeChanged { get { return typeChanged; } } /// /// The total number of lines added in this diff. /// public virtual int LinesAdded { get { return linesAdded; } } /// /// The total number of lines added in this diff. /// public virtual int LinesDeleted { get { return linesDeleted; } } /// /// The full patch file of this diff. /// public virtual string Patch { get { return fullPatchBuilder.ToString(); } } private string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "+{0} ~{2} -{1} \u00B1{3}", Added.Count(), Deleted.Count(), Modified.Count(), TypeChanged.Count()); } } } }