using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Compat; namespace LibGit2Sharp { /// /// Holds the result of the determination of the state of the working directory. /// Only files that differ from the current index and/or commit will be considered. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public class RepositoryStatus : IEnumerable { private readonly ICollection statusEntries; private readonly List added = new List(); private readonly List staged = new List(); private readonly List removed = new List(); private readonly List missing = new List(); private readonly List modified = new List(); private readonly List untracked = new List(); private readonly List ignored = new List(); private readonly bool isDirty; private readonly IDictionary> dispatcher = Build(); private static IDictionary> Build() { return new Dictionary> { { FileStatus.Untracked, (rs, s) => rs.untracked.Add(s) }, { FileStatus.Modified, (rs, s) => rs.modified.Add(s) }, { FileStatus.Missing, (rs, s) => rs.missing.Add(s) }, { FileStatus.Added, (rs, s) => rs.added.Add(s) }, { FileStatus.Staged, (rs, s) => rs.staged.Add(s) }, { FileStatus.Removed, (rs, s) => rs.removed.Add(s) }, { FileStatus.Ignored, (rs, s) => rs.ignored.Add(s) }, }; } /// /// Needed for mocking purposes. /// protected RepositoryStatus() { } internal RepositoryStatus(Repository repo) { statusEntries = Proxy.git_status_foreach(repo.Handle, StateChanged); isDirty = statusEntries.Any(entry => entry.State != FileStatus.Ignored); } private StatusEntry StateChanged(IntPtr filePathPtr, uint state) { var filePath = FilePathMarshaler.FromNative(filePathPtr); var gitStatus = (FileStatus)state; foreach (KeyValuePair> kvp in dispatcher) { if (!gitStatus.HasFlag(kvp.Key)) { continue; } kvp.Value(this, filePath.Native); } return new StatusEntry(filePath.Native, gitStatus); } /// /// Gets the for the specified relative path. /// public virtual FileStatus this[string path] { get { Ensure.ArgumentNotNullOrEmptyString(path, "path"); var entries = statusEntries.Where(e => string.Equals(e.FilePath, path, StringComparison.Ordinal)).ToList(); Debug.Assert(!(entries.Count > 1)); if (entries.Count == 0) { return FileStatus.Nonexistent; } return entries.Single().State; } } /// /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. public virtual IEnumerator GetEnumerator() { return statusEntries.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(); } /// /// List of files added to the index, which are not in the current commit /// public virtual IEnumerable Added { get { return added; } } /// /// List of files added to the index, which are already in the current commit with different content /// public virtual IEnumerable Staged { get { return staged; } } /// /// List of files removed from the index but are existent in the current commit /// public virtual IEnumerable Removed { get { return removed; } } /// /// List of files existent in the index but are missing in the working directory /// public virtual IEnumerable Missing { get { return missing; } } /// /// List of files with unstaged modifications. A file may be modified and staged at the same time if it has been modified after adding. /// public virtual IEnumerable Modified { get { return modified; } } /// /// List of files existing in the working directory but are neither tracked in the index nor in the current commit. /// public virtual IEnumerable Untracked { get { return untracked; } } /// /// List of files existing in the working directory that are ignored. /// public virtual IEnumerable Ignored { get { return ignored; } } /// /// True if the index or the working directory has been altered since the last commit. False otherwise. /// public virtual bool IsDirty { get { return isDirty; } } private string DebuggerDisplay { get { return string.Format( CultureInfo.InvariantCulture, "+{0} ~{1} -{2} | +{3} ~{4} -{5} | i{6}", Added.Count(), Staged.Count(), Removed.Count(), Untracked.Count(), Modified.Count(), Missing.Count(), Ignored.Count()); } } } }