/// <summary> /// Compares two commits and returns a list of files that have changed /// </summary> public static IEnumerable <Change> CompareCommits(NGit.Repository repo, RevCommit reference, RevCommit compared) { var changes = new List <Change>(); if (reference == null && compared == null) { return(changes); } ObjectId refTree = (reference != null ? reference.Tree.Id : ObjectId.ZeroId); ObjectId comparedTree = (compared != null ? compared.Tree.Id : ObjectId.ZeroId); var walk = new TreeWalk(repo); if (reference == null || compared == null) { walk.Reset((reference ?? compared).Tree.Id); } else { walk.Reset(new AnyObjectId[] { refTree, comparedTree }); } walk.Recursive = true; walk.Filter = AndTreeFilter.Create(TreeFilter.ANY_DIFF, TreeFilter.ALL); return(CalculateCommitDiff(repo, walk, new[] { reference, compared })); }
/// <summary> /// Executes the /// <code>Log</code> /// command with all the options and parameters /// collected by the setter methods (e.g. /// <see cref="Add(NGit.AnyObjectId)">Add(NGit.AnyObjectId)</see> /// , /// <see cref="Not(NGit.AnyObjectId)">Not(NGit.AnyObjectId)</see> /// , ..) of this class. Each instance of this class /// should only be used for one invocation of the command. Don't call this /// method twice on an instance. /// </summary> /// <returns>an iteration over RevCommits</returns> /// <exception cref="NGit.Api.Errors.NoHeadException"></exception> /// <exception cref="NGit.Api.Errors.JGitInternalException"></exception> public override Iterable <RevCommit> Call() { CheckCallable(); if (pathFilters.Count > 0) { walk.SetTreeFilter(AndTreeFilter.Create(PathFilterGroup.Create(pathFilters), TreeFilter .ANY_DIFF)); } if (!startSpecified) { try { ObjectId headId = repo.Resolve(Constants.HEAD); if (headId == null) { throw new NoHeadException(JGitText.Get().noHEADExistsAndNoExplicitStartingRevisionWasSpecified ); } Add(headId); } catch (IOException e) { // all exceptions thrown by add() shouldn't occur and represent // severe low-level exception which are therefore wrapped throw new JGitInternalException(JGitText.Get().anExceptionOccurredWhileTryingToAddTheIdOfHEAD , e); } } SetCallable(false); return(walk); }
internal IList <GitFile> GetChangedFiles() { if (!HasGitRepository) { return(new List <GitFile>()); } if (GitBash.Exists) { var output = GitBash.Run("status --porcelain -z --untracked-files", this.GitWorkingDirectory); return(ParseGitStatus(output)); } else { var list = new List <GitFile>(); var treeWalk = new TreeWalk(this.repository); treeWalk.Recursive = true; treeWalk.Filter = TreeFilter.ANY_DIFF; var id = repository.Resolve(Constants.HEAD); if (id != null) { treeWalk.AddTree(new RevWalk(repository).ParseTree(id)); } else { treeWalk.AddTree(new EmptyTreeIterator()); } treeWalk.AddTree(new DirCacheIterator(this.repository.ReadDirCache())); treeWalk.AddTree(new FileTreeIterator(this.repository)); var filters = new TreeFilter[] { new SkipWorkTreeFilter(INDEX), new IndexDiffFilter(INDEX, WORKDIR) }; treeWalk.Filter = AndTreeFilter.Create(filters); while (treeWalk.Next()) { WorkingTreeIterator workingTreeIterator = treeWalk.GetTree <WorkingTreeIterator>(WORKDIR); if (workingTreeIterator != null && workingTreeIterator.IsEntryIgnored()) { continue; } var fileName = GetFullPath(treeWalk.PathString); if (Directory.Exists(fileName)) { continue; // this excludes sub modules } var status = GetFileStatus(treeWalk); list.Add(new GitFile { FileName = GetRelativeFileName(fileName), Status = status }); this.cache[GetCacheKey(fileName)] = status; } return(list); } }
public GitFileStatus GetFileStatusNoCache(string fileName) { if (Directory.Exists(fileName)) { return(GitFileStatus.Ignored); } var fileNameRel = GetRelativeFileNameForGit(fileName); TreeWalk treeWalk = new TreeWalk(this.repository) { Recursive = true }; RevTree revTree = head == null ? null : new RevWalk(repository).ParseTree(head); if (revTree != null) { treeWalk.AddTree(revTree); } else { treeWalk.AddTree(new EmptyTreeIterator()); } treeWalk.AddTree(new DirCacheIterator(dirCache)); treeWalk.AddTree(new FileTreeIterator(this.repository)); var filters = new TreeFilter[] { PathFilter.Create(fileNameRel), new SkipWorkTreeFilter(INDEX), new IndexDiffFilter(INDEX, WORKDIR) }; treeWalk.Filter = AndTreeFilter.Create(filters); var status = GitFileStatus.NotControlled; if (treeWalk.Next()) { status = GetFileStatus(treeWalk); } if (status == GitFileStatus.NotControlled) { var dirCacheEntry2 = dirCache.GetEntry(fileNameRel); if (dirCacheEntry2 != null) { var treeEntry2 = TreeWalk.ForPath(repository, fileNameRel, revTree); if (treeEntry2 != null && treeEntry2.GetObjectId(0).Equals(dirCacheEntry2.GetObjectId())) { return(GitFileStatus.Tracked); } } } return(GitFileStatus.NotControlled); }
/// <summary> /// Returns a list of files that have changed in a commit /// </summary> public static IEnumerable <Change> GetCommitChanges(NGit.Repository repo, RevCommit commit) { var treeIds = new[] { commit.Tree.Id }.Concat(commit.Parents.Select(c => c.Tree.Id)).ToArray(); var walk = new TreeWalk(repo); walk.Reset(treeIds); walk.Recursive = true; walk.Filter = AndTreeFilter.Create(AndTreeFilter.ANY_DIFF, AndTreeFilter.ALL); return(CalculateCommitDiff(repo, walk, new[] { commit }.Concat(commit.Parents).ToArray())); }
/// <summary>Determine the differences between two trees.</summary> /// <remarks> /// Determine the differences between two trees. /// No output is created, instead only the file paths that are different are /// returned. Callers may choose to format these paths themselves, or convert /// them into /// <see cref="NGit.Patch.FileHeader">NGit.Patch.FileHeader</see> /// instances with a complete edit list by /// calling /// <see cref="ToFileHeader(DiffEntry)">ToFileHeader(DiffEntry)</see> /// . /// </remarks> /// <param name="a">the old (or previous) side.</param> /// <param name="b">the new (or updated) side.</param> /// <returns>the paths that are different.</returns> /// <exception cref="System.IO.IOException">trees cannot be read or file contents cannot be read. /// </exception> public virtual IList <DiffEntry> Scan(AbstractTreeIterator a, AbstractTreeIterator b) { AssertHaveRepository(); TreeWalk walk = new TreeWalk(reader); walk.AddTree(a); walk.AddTree(b); walk.Recursive = true; TreeFilter filter = GetDiffTreeFilterFor(a, b); if (pathFilter is FollowFilter) { walk.Filter = AndTreeFilter.Create(PathFilter.Create(((FollowFilter)pathFilter).GetPath ()), filter); } else { walk.Filter = AndTreeFilter.Create(pathFilter, filter); } source = new ContentSource.Pair(Source(a), Source(b)); IList <DiffEntry> files = DiffEntry.Scan(walk); if (pathFilter is FollowFilter && IsAdd(files)) { // The file we are following was added here, find where it // came from so we can properly show the rename or copy, // then continue digging backwards. // a.Reset(); b.Reset(); walk.Reset(); walk.AddTree(a); walk.AddTree(b); walk.Filter = filter; if (renameDetector == null) { SetDetectRenames(true); } files = UpdateFollowFilter(DetectRenames(DiffEntry.Scan(walk))); } else { if (renameDetector != null) { files = DetectRenames(files); } } return(files); }
private static TreeFilter GetDiffTreeFilterFor(AbstractTreeIterator a, AbstractTreeIterator b) { if (a is DirCacheIterator && b is WorkingTreeIterator) { return(new IndexDiffFilter(0, 1)); } if (a is WorkingTreeIterator && b is DirCacheIterator) { return(new IndexDiffFilter(1, 0)); } TreeFilter filter = TreeFilter.ANY_DIFF; if (a is WorkingTreeIterator) { filter = AndTreeFilter.Create(new NotIgnoredFilter(0), filter); } if (b is WorkingTreeIterator) { filter = AndTreeFilter.Create(new NotIgnoredFilter(1), filter); } return(filter); }
/// <summary> /// Run the diff operation. Until this is called, all lists will be empty /// </summary> /// <returns>true if anything is different between index, tree, and workdir</returns> private void UpdateDirectory(IEnumerable <string> paths, bool recursive) { RevWalk rw = new RevWalk(Repository); ObjectId id = Repository.Resolve(Constants.HEAD); var commit = id != null?rw.ParseCommit(id) : null; TreeWalk treeWalk = new TreeWalk(Repository); treeWalk.Reset(); treeWalk.Recursive = false; if (commit != null) { treeWalk.AddTree(commit.Tree); } else { treeWalk.AddTree(new EmptyTreeIterator()); } DirCache dc = Repository.ReadDirCache(); treeWalk.AddTree(new DirCacheIterator(dc)); FileTreeIterator workTree = new FileTreeIterator(Repository.WorkTree, Repository.FileSystem, WorkingTreeOptions.KEY.Parse(Repository.GetConfig())); treeWalk.AddTree(workTree); List <TreeFilter> filters = new List <TreeFilter> (); filters.Add(new SkipWorkTreeFilter(1)); var pathFilters = paths.Where(p => p != ".").Select(p => PathFilter.Create(p)).ToArray(); if (pathFilters.Length > 1) { filters.Add(OrTreeFilter.Create(pathFilters)); // Use an OR to join all path filters } else if (pathFilters.Length == 1) { filters.Add(pathFilters[0]); } if (filters.Count > 1) { treeWalk.Filter = AndTreeFilter.Create(filters); } else { treeWalk.Filter = filters[0]; } while (treeWalk.Next()) { AbstractTreeIterator treeIterator = treeWalk.GetTree <AbstractTreeIterator>(0); DirCacheIterator dirCacheIterator = treeWalk.GetTree <DirCacheIterator>(1); WorkingTreeIterator workingTreeIterator = treeWalk.GetTree <WorkingTreeIterator>(2); NGit.FileMode fileModeTree = treeWalk.GetFileMode(0); if (treeWalk.IsSubtree) { treeWalk.EnterSubtree(); continue; } int stage = dirCacheIterator != null?dirCacheIterator.GetDirCacheEntry().Stage : 0; if (stage > 1) { continue; } else if (stage == 1) { MergeConflict.Add(dirCacheIterator.EntryPathString); changesExist = true; continue; } if (treeIterator != null) { if (dirCacheIterator != null) { if (!treeIterator.EntryObjectId.Equals(dirCacheIterator.EntryObjectId)) { // in repo, in index, content diff => changed Modified.Add(dirCacheIterator.EntryPathString); changesExist = true; } } else { // in repo, not in index => removed if (!fileModeTree.Equals(NGit.FileMode.TYPE_TREE)) { Removed.Add(treeIterator.EntryPathString); changesExist = true; } } } else { if (dirCacheIterator != null) { // not in repo, in index => added Added.Add(dirCacheIterator.EntryPathString); changesExist = true; } else { // not in repo, not in index => untracked if (workingTreeIterator != null && !workingTreeIterator.IsEntryIgnored()) { Untracked.Add(workingTreeIterator.EntryPathString); changesExist = true; } } } if (dirCacheIterator != null) { if (workingTreeIterator == null) { // in index, not in workdir => missing Missing.Add(dirCacheIterator.EntryPathString); changesExist = true; } else { // Workaround to file time resolution issues long itime = dirCacheIterator.GetDirCacheEntry().LastModified; long ftime = workingTreeIterator.GetEntryLastModified(); if (itime / 1000 != ftime / 1000) { if (!dirCacheIterator.IdEqual(workingTreeIterator)) { // in index, in workdir, content differs => modified Modified.Add(dirCacheIterator.EntryPathString); changesExist = true; } } } } } }
/// <summary>Run the diff operation.</summary> /// <remarks> /// Run the diff operation. Until this is called, all lists will be empty. /// <p> /// The operation may be aborted by the progress monitor. In that event it /// will report what was found before the cancel operation was detected. /// Callers should ignore the result if monitor.isCancelled() is true. If a /// progress monitor is not needed, callers should use /// <see cref="Diff()">Diff()</see> /// instead. Progress reporting is crude and approximate and only intended /// for informing the user. /// </remarks> /// <param name="monitor">for reporting progress, may be null</param> /// <param name="estWorkTreeSize">number or estimated files in the working tree</param> /// <param name="estIndexSize">number of estimated entries in the cache</param> /// <param name="title"></param> /// <returns>if anything is different between index, tree, and workdir</returns> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> public virtual bool Diff(ProgressMonitor monitor, int estWorkTreeSize, int estIndexSize , string title) { dirCache = repository.ReadDirCache(); TreeWalk treeWalk = new TreeWalk(repository); treeWalk.Recursive = true; // add the trees (tree, dirchache, workdir) if (tree != null) { treeWalk.AddTree(tree); } else { treeWalk.AddTree(new EmptyTreeIterator()); } treeWalk.AddTree(new DirCacheIterator(dirCache)); treeWalk.AddTree(initialWorkingTreeIterator); ICollection <TreeFilter> filters = new AList <TreeFilter>(4); if (monitor != null) { // Get the maximum size of the work tree and index // and add some (quite arbitrary) if (estIndexSize == 0) { estIndexSize = dirCache.GetEntryCount(); } int total = Math.Max(estIndexSize * 10 / 9, estWorkTreeSize * 10 / 9); monitor.BeginTask(title, total); filters.AddItem(new IndexDiff.ProgressReportingFilter(monitor, total)); } if (filter != null) { filters.AddItem(filter); } filters.AddItem(new SkipWorkTreeFilter(INDEX)); filters.AddItem(new IndexDiffFilter(INDEX, WORKDIR)); treeWalk.Filter = AndTreeFilter.Create(filters); while (treeWalk.Next()) { AbstractTreeIterator treeIterator = treeWalk.GetTree <AbstractTreeIterator>(TREE); DirCacheIterator dirCacheIterator = treeWalk.GetTree <DirCacheIterator>(INDEX); WorkingTreeIterator workingTreeIterator = treeWalk.GetTree <WorkingTreeIterator>(WORKDIR ); if (dirCacheIterator != null) { DirCacheEntry dirCacheEntry = dirCacheIterator.GetDirCacheEntry(); if (dirCacheEntry != null && dirCacheEntry.Stage > 0) { conflicts.AddItem(treeWalk.PathString); continue; } } if (treeIterator != null) { if (dirCacheIterator != null) { if (!treeIterator.IdEqual(dirCacheIterator) || treeIterator.EntryRawMode != dirCacheIterator .EntryRawMode) { // in repo, in index, content diff => changed changed.AddItem(treeWalk.PathString); } } else { // in repo, not in index => removed removed.AddItem(treeWalk.PathString); if (workingTreeIterator != null) { untracked.AddItem(treeWalk.PathString); } } } else { if (dirCacheIterator != null) { // not in repo, in index => added added.AddItem(treeWalk.PathString); } else { // not in repo, not in index => untracked if (workingTreeIterator != null && !workingTreeIterator.IsEntryIgnored()) { untracked.AddItem(treeWalk.PathString); } } } if (dirCacheIterator != null) { if (workingTreeIterator == null) { // in index, not in workdir => missing missing.AddItem(treeWalk.PathString); } else { if (workingTreeIterator.IsModified(dirCacheIterator.GetDirCacheEntry(), true)) { // in index, in workdir, content differs => modified modified.AddItem(treeWalk.PathString); } } } } // consume the remaining work if (monitor != null) { monitor.EndTask(); } if (added.IsEmpty() && changed.IsEmpty() && removed.IsEmpty() && missing.IsEmpty( ) && modified.IsEmpty() && untracked.IsEmpty()) { return(false); } else { return(true); } }
/// <summary> /// Stash the contents on the working directory and index in separate commits /// and reset to the current HEAD commit. /// </summary> /// <remarks> /// Stash the contents on the working directory and index in separate commits /// and reset to the current HEAD commit. /// </remarks> /// <returns>stashed commit or null if no changes to stash</returns> /// <exception cref="NGit.Api.Errors.GitAPIException">NGit.Api.Errors.GitAPIException /// </exception> public override RevCommit Call() { CheckCallable(); Ref head = GetHead(); ObjectReader reader = repo.NewObjectReader(); try { RevCommit headCommit = ParseCommit(reader, head.GetObjectId()); DirCache cache = repo.LockDirCache(); ObjectInserter inserter = repo.NewObjectInserter(); ObjectId commitId; try { TreeWalk treeWalk = new TreeWalk(reader); treeWalk.Recursive = true; treeWalk.AddTree(headCommit.Tree); treeWalk.AddTree(new DirCacheIterator(cache)); treeWalk.AddTree(new FileTreeIterator(repo)); treeWalk.Filter = AndTreeFilter.Create(new SkipWorkTreeFilter(1), new IndexDiffFilter (1, 2)); // Return null if no local changes to stash if (!treeWalk.Next()) { return(null); } MutableObjectId id = new MutableObjectId(); IList <DirCacheEditor.PathEdit> wtEdits = new AList <DirCacheEditor.PathEdit>(); IList <string> wtDeletes = new AList <string>(); do { AbstractTreeIterator headIter = treeWalk.GetTree <AbstractTreeIterator>(0); DirCacheIterator indexIter = treeWalk.GetTree <DirCacheIterator>(1); WorkingTreeIterator wtIter = treeWalk.GetTree <WorkingTreeIterator>(2); if (headIter != null && indexIter != null && wtIter != null) { if (!indexIter.GetDirCacheEntry().IsMerged()) { throw new UnmergedPathsException(new UnmergedPathException(indexIter.GetDirCacheEntry ())); } if (wtIter.IdEqual(indexIter) || wtIter.IdEqual(headIter)) { continue; } treeWalk.GetObjectId(id, 0); DirCacheEntry entry = new DirCacheEntry(treeWalk.RawPath); entry.SetLength(wtIter.GetEntryLength()); entry.LastModified = wtIter.GetEntryLastModified(); entry.FileMode = wtIter.EntryFileMode; long contentLength = wtIter.GetEntryContentLength(); InputStream @in = wtIter.OpenEntryStream(); try { entry.SetObjectId(inserter.Insert(Constants.OBJ_BLOB, contentLength, @in)); } finally { @in.Close(); } wtEdits.AddItem(new _PathEdit_273(entry, entry)); } else { if (indexIter == null) { wtDeletes.AddItem(treeWalk.PathString); } else { if (wtIter == null && headIter != null) { wtDeletes.AddItem(treeWalk.PathString); } } } }while (treeWalk.Next()); string branch = Repository.ShortenRefName(head.GetTarget().GetName()); // Commit index changes NGit.CommitBuilder builder = CreateBuilder(headCommit); builder.TreeId = cache.WriteTree(inserter); builder.Message = MessageFormat.Format(indexMessage, branch, headCommit.Abbreviate (7).Name, headCommit.GetShortMessage()); ObjectId indexCommit = inserter.Insert(builder); // Commit working tree changes if (!wtEdits.IsEmpty() || !wtDeletes.IsEmpty()) { DirCacheEditor editor = cache.Editor(); foreach (DirCacheEditor.PathEdit edit in wtEdits) { editor.Add(edit); } foreach (string path in wtDeletes) { editor.Add(new DirCacheEditor.DeletePath(path)); } editor.Finish(); } builder.AddParentId(indexCommit); builder.Message = MessageFormat.Format(workingDirectoryMessage, branch, headCommit .Abbreviate(7).Name, headCommit.GetShortMessage()); builder.TreeId = cache.WriteTree(inserter); commitId = inserter.Insert(builder); inserter.Flush(); UpdateStashRef(commitId, builder.Author, builder.Message); } finally { inserter.Release(); cache.Unlock(); } // Hard reset to HEAD new ResetCommand(repo).SetMode(ResetCommand.ResetType.HARD).Call(); // Return stashed commit return(ParseCommit(reader, commitId)); } catch (IOException e) { throw new JGitInternalException(JGitText.Get().stashFailed, e); } finally { reader.Release(); } }
protected internal virtual void Filter(string path) { rw.SetTreeFilter(AndTreeFilter.Create(PathFilterGroup.CreateFromStrings(Sharpen.Collections .Singleton(path)), TreeFilter.ANY_DIFF)); }