Пример #1
0
        ObjectId WriteWorkingDirectoryTree(RevTree headTree, DirCache index)
        {
            DirCache        dc = DirCache.NewInCore();
            DirCacheBuilder cb = dc.Builder();

            ObjectInserter oi = _repo.NewObjectInserter();

            try {
                TreeWalk tw = new TreeWalk(_repo);
                tw.Reset();
                tw.AddTree(new FileTreeIterator(_repo));
                tw.AddTree(headTree);
                tw.AddTree(new DirCacheIterator(index));

                while (tw.Next())
                {
                    // Ignore untracked files
                    if (tw.IsSubtree)
                    {
                        tw.EnterSubtree();
                    }
                    else if (tw.GetFileMode(0) != NGit.FileMode.MISSING && (tw.GetFileMode(1) != NGit.FileMode.MISSING || tw.GetFileMode(2) != NGit.FileMode.MISSING))
                    {
                        WorkingTreeIterator f            = tw.GetTree <WorkingTreeIterator>(0);
                        DirCacheIterator    dcIter       = tw.GetTree <DirCacheIterator>(2);
                        DirCacheEntry       currentEntry = dcIter.GetDirCacheEntry();
                        DirCacheEntry       ce           = new DirCacheEntry(tw.PathString);
                        if (!f.IsModified(currentEntry, true))
                        {
                            ce.SetLength(currentEntry.Length);
                            ce.LastModified = currentEntry.LastModified;
                            ce.FileMode     = currentEntry.FileMode;
                            ce.SetObjectId(currentEntry.GetObjectId());
                        }
                        else
                        {
                            long sz = f.GetEntryLength();
                            ce.SetLength(sz);
                            ce.LastModified = f.GetEntryLastModified();
                            ce.FileMode     = f.EntryFileMode;
                            var data = f.OpenEntryStream();
                            try {
                                ce.SetObjectId(oi.Insert(Constants.OBJ_BLOB, sz, data));
                            } finally {
                                data.Close();
                            }
                        }
                        cb.Add(ce);
                    }
                }

                cb.Finish();
                return(dc.WriteTree(oi));
            } finally {
                oi.Release();
            }
        }
Пример #2
0
        public virtual void TestUpdateSmudgedEntries()
        {
            git.BranchCreate().SetName("test2").Call();
            RefUpdate rup = db.UpdateRef(Constants.HEAD);

            rup.Link("refs/heads/test2");
            FilePath file  = new FilePath(db.WorkTree, "Test.txt");
            long     size  = file.Length();
            long     mTime = file.LastModified() - 5000L;

            NUnit.Framework.Assert.IsTrue(file.SetLastModified(mTime));
            DirCache      cache = DirCache.Lock(db.GetIndexFile(), db.FileSystem);
            DirCacheEntry entry = cache.GetEntry("Test.txt");

            NUnit.Framework.Assert.IsNotNull(entry);
            entry.SetLength(0);
            entry.LastModified = 0;
            cache.Write();
            NUnit.Framework.Assert.IsTrue(cache.Commit());
            cache = DirCache.Read(db.GetIndexFile(), db.FileSystem);
            entry = cache.GetEntry("Test.txt");
            NUnit.Framework.Assert.IsNotNull(entry);
            NUnit.Framework.Assert.AreEqual(0, entry.Length);
            NUnit.Framework.Assert.AreEqual(0, entry.LastModified);
            db.GetIndexFile().SetLastModified(db.GetIndexFile().LastModified() - 5000);
            NUnit.Framework.Assert.IsNotNull(git.Checkout().SetName("test").Call());
            cache = DirCache.Read(db.GetIndexFile(), db.FileSystem);
            entry = cache.GetEntry("Test.txt");
            NUnit.Framework.Assert.IsNotNull(entry);
            NUnit.Framework.Assert.AreEqual(size, entry.Length);
            NUnit.Framework.Assert.AreEqual(mTime, entry.LastModified);
        }
Пример #3
0
        /// <summary>
        /// adds a entry to the index builder which is a copy of the specified
        /// DirCacheEntry
        /// </summary>
        /// <param name="e">the entry which should be copied</param>
        /// <returns>the entry which was added to the index</returns>
        private DirCacheEntry Keep(DirCacheEntry e)
        {
            DirCacheEntry newEntry = new DirCacheEntry(e.PathString, e.Stage);

            newEntry.FileMode = e.FileMode;
            newEntry.SetObjectId(e.GetObjectId());
            newEntry.LastModified = e.LastModified;
            newEntry.SetLength(e.Length);
            builder.Add(newEntry);
            return(newEntry);
        }
Пример #4
0
 /// <summary>adds a new path with the specified stage to the index builder</summary>
 /// <param name="path"></param>
 /// <param name="p"></param>
 /// <param name="stage"></param>
 /// <param name="lastMod"></param>
 /// <param name="len"></param>
 /// <returns>the entry which was added to the index</returns>
 private DirCacheEntry Add(byte[] path, CanonicalTreeParser p, int stage, long lastMod
                           , long len)
 {
     if (p != null && !p.EntryFileMode.Equals(FileMode.TREE))
     {
         DirCacheEntry e = new DirCacheEntry(path, stage);
         e.FileMode = p.EntryFileMode;
         e.SetObjectId(p.EntryObjectId);
         e.LastModified = lastMod;
         e.SetLength(len);
         builder.Add(e);
         return(e);
     }
     return(null);
 }
Пример #5
0
        /// <exception cref="System.IO.IOException"></exception>
        private DirCacheEntry AddEntryToBuilder(string path, FilePath file, ObjectInserter
                                                newObjectInserter, DirCacheBuilder builder, int stage)
        {
            FileInputStream inputStream = new FileInputStream(file);
            ObjectId        id          = newObjectInserter.Insert(Constants.OBJ_BLOB, file.Length(), inputStream
                                                                   );

            inputStream.Close();
            DirCacheEntry entry = new DirCacheEntry(path, stage);

            entry.SetObjectId(id);
            entry.FileMode     = FileMode.REGULAR_FILE;
            entry.LastModified = file.LastModified();
            entry.SetLength((int)file.Length());
            builder.Add(entry);
            return(entry);
        }
Пример #6
0
        /// <exception cref="System.IO.IOException"></exception>
        private void ResetIndex(RevCommit commit)
        {
            DirCache dc   = repo.LockDirCache();
            TreeWalk walk = null;

            try
            {
                DirCacheEditor editor = dc.Editor();
                walk = new TreeWalk(repo);
                walk.AddTree(commit.Tree);
                walk.AddTree(new DirCacheIterator(dc));
                walk.Recursive = true;
                while (walk.Next())
                {
                    AbstractTreeIterator cIter = walk.GetTree <AbstractTreeIterator>(0);
                    if (cIter == null)
                    {
                        editor.Add(new DirCacheEditor.DeletePath(walk.PathString));
                        continue;
                    }
                    DirCacheEntry entry = new DirCacheEntry(walk.RawPath);
                    entry.FileMode = cIter.EntryFileMode;
                    entry.SetObjectIdFromRaw(cIter.IdBuffer, cIter.IdOffset);
                    DirCacheIterator dcIter = walk.GetTree <DirCacheIterator>(1);
                    if (dcIter != null && dcIter.IdEqual(cIter))
                    {
                        DirCacheEntry indexEntry = dcIter.GetDirCacheEntry();
                        entry.LastModified = indexEntry.LastModified;
                        entry.SetLength(indexEntry.Length);
                    }
                    editor.Add(new _PathEdit_356(entry, entry));
                }
                editor.Commit();
            }
            finally
            {
                dc.Unlock();
                if (walk != null)
                {
                    walk.Release();
                }
            }
        }
Пример #7
0
 /// <summary>Updates the index after a content merge has happened.</summary>
 /// <remarks>
 /// Updates the index after a content merge has happened. If no conflict has
 /// occurred this includes persisting the merged content to the object
 /// database. In case of conflicts this method takes care to write the
 /// correct stages to the index.
 /// </remarks>
 /// <param name="base"></param>
 /// <param name="ours"></param>
 /// <param name="theirs"></param>
 /// <param name="result"></param>
 /// <param name="of"></param>
 /// <exception cref="System.IO.FileNotFoundException">System.IO.FileNotFoundException
 ///     </exception>
 /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 private void UpdateIndex(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser
                          theirs, MergeResult <RawText> result, FilePath of)
 {
     if (result.ContainsConflicts())
     {
         // a conflict occurred, the file will contain conflict markers
         // the index will be populated with the three stages and only the
         // workdir (if used) contains the halfways merged content
         Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0);
         Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0);
         Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0);
         mergeResults.Put(tw.PathString, result.Upcast());
     }
     else
     {
         // no conflict occurred, the file will contain fully merged content.
         // the index will be populated with the new merged version
         DirCacheEntry dce     = new DirCacheEntry(tw.PathString);
         int           newMode = MergeFileModes(tw.GetRawMode(0), tw.GetRawMode(1), tw.GetRawMode(2)
                                                );
         // set the mode for the new content. Fall back to REGULAR_FILE if
         // you can't merge modes of OURS and THEIRS
         dce.FileMode = (newMode == FileMode.MISSING.GetBits()) ? FileMode.REGULAR_FILE :
                        FileMode.FromBits(newMode);
         dce.LastModified = of.LastModified();
         dce.SetLength((int)of.Length());
         InputStream @is = new FileInputStream(of);
         try
         {
             dce.SetObjectId(GetObjectInserter().Insert(Constants.OBJ_BLOB, of.Length(), @is));
         }
         finally
         {
             @is.Close();
             if (inCore)
             {
                 FileUtils.Delete(of);
             }
         }
         builder.Add(dce);
     }
 }
Пример #8
0
 /// <summary>Compares the entries content with the content in the filesystem.</summary>
 /// <remarks>
 /// Compares the entries content with the content in the filesystem.
 /// Unsmudges the entry when it is detected that it is clean.
 /// </remarks>
 /// <param name="entry">the entry to be checked</param>
 /// <returns>
 /// <code>true</code> if the content matches, <code>false</code>
 /// otherwise
 /// </returns>
 private bool ContentCheck(DirCacheEntry entry)
 {
     if (EntryObjectId.Equals(entry.GetObjectId()))
     {
         // Content has not changed
         // We know the entry can't be racily clean because it's still clean.
         // Therefore we unsmudge the entry!
         // If by any chance we now unsmudge although we are still in the
         // same time-slot as the last modification to the index file the
         // next index write operation will smudge again.
         // Caution: we are unsmudging just by setting the length of the
         // in-memory entry object. It's the callers task to detect that we
         // have modified the entry and to persist the modified index.
         entry.SetLength((int)GetEntryLength());
         return(false);
     }
     else
     {
         // Content differs: that's a real change!
         return(true);
     }
 }
Пример #9
0
        /// <summary>Resets the index to represent exactly some filesystem content.</summary>
        /// <remarks>
        /// Resets the index to represent exactly some filesystem content. E.g. the
        /// following call will replace the index with the working tree content:
        /// <p>
        /// <code>resetIndex(new FileSystemIterator(db))</code>
        /// <p>
        /// This method can be used by testcases which first prepare a new commit
        /// somewhere in the filesystem (e.g. in the working-tree) and then want to
        /// have an index which matches their prepared content.
        /// </remarks>
        /// <param name="treeItr">
        /// a
        /// <see cref="NGit.Treewalk.FileTreeIterator">NGit.Treewalk.FileTreeIterator</see>
        /// which determines which files should
        /// go into the new index
        /// </param>
        /// <exception cref="System.IO.FileNotFoundException">System.IO.FileNotFoundException
        ///     </exception>
        /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
        protected internal virtual void ResetIndex(FileTreeIterator treeItr)
        {
            ObjectInserter  inserter = db.NewObjectInserter();
            DirCacheBuilder builder  = db.LockDirCache().Builder();
            DirCacheEntry   dce;

            while (!treeItr.Eof)
            {
                long len = treeItr.GetEntryLength();
                dce              = new DirCacheEntry(treeItr.EntryPathString);
                dce.FileMode     = treeItr.EntryFileMode;
                dce.LastModified = treeItr.GetEntryLastModified();
                dce.SetLength((int)len);
                FileInputStream @in = new FileInputStream(treeItr.GetEntryFile());
                dce.SetObjectId(inserter.Insert(Constants.OBJ_BLOB, len, @in));
                @in.Close();
                builder.Add(dce);
                treeItr.Next(1);
            }
            builder.Commit();
            inserter.Flush();
            inserter.Release();
        }
Пример #10
0
        /// <summary>
        /// Executes the
        /// <code>Add</code>
        /// command. 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>the DirCache after Add</returns>
        /// <exception cref="NGit.Api.Errors.GitAPIException"></exception>
        /// <exception cref="NGit.Api.Errors.NoFilepatternException"></exception>
        public override DirCache Call()
        {
            if (filepatterns.IsEmpty())
            {
                throw new NoFilepatternException(JGitText.Get().atLeastOnePatternIsRequired);
            }
            CheckCallable();
            DirCache dc     = null;
            bool     addAll = false;

            if (filepatterns.Contains("."))
            {
                addAll = true;
            }
            ObjectInserter inserter = repo.NewObjectInserter();

            try
            {
                dc = repo.LockDirCache();
                DirCacheIterator c;
                DirCacheBuilder  builder = dc.Builder();
                TreeWalk         tw      = new TreeWalk(repo);
                tw.AddTree(new DirCacheBuildIterator(builder));
                if (workingTreeIterator == null)
                {
                    workingTreeIterator = new FileTreeIterator(repo);
                }
                tw.AddTree(workingTreeIterator);
                tw.Recursive = true;
                if (!addAll)
                {
                    tw.Filter = PathFilterGroup.CreateFromStrings(filepatterns);
                }
                string lastAddedFile = null;
                while (tw.Next())
                {
                    string path           = tw.PathString;
                    WorkingTreeIterator f = tw.GetTree <WorkingTreeIterator>(1);
                    if (tw.GetTree <DirCacheIterator>(0) == null && f != null && f.IsEntryIgnored())
                    {
                    }
                    else
                    {
                        // file is not in index but is ignored, do nothing
                        // In case of an existing merge conflict the
                        // DirCacheBuildIterator iterates over all stages of
                        // this path, we however want to add only one
                        // new DirCacheEntry per path.
                        if (!(path.Equals(lastAddedFile)))
                        {
                            if (!(update && tw.GetTree <DirCacheIterator>(0) == null))
                            {
                                c = tw.GetTree <DirCacheIterator>(0);
                                if (f != null)
                                {
                                    // the file exists
                                    long          sz    = f.GetEntryLength();
                                    DirCacheEntry entry = new DirCacheEntry(path);
                                    if (c == null || c.GetDirCacheEntry() == null || !c.GetDirCacheEntry().IsAssumeValid)
                                    {
                                        FileMode mode = f.GetIndexFileMode(c);
                                        entry.FileMode = mode;
                                        if (FileMode.GITLINK != mode)
                                        {
                                            entry.SetLength(sz);
                                            entry.LastModified = f.GetEntryLastModified();
                                            long        contentSize = f.GetEntryContentLength();
                                            InputStream @in         = f.OpenEntryStream();
                                            try
                                            {
                                                entry.SetObjectId(inserter.Insert(Constants.OBJ_BLOB, contentSize, @in));
                                            }
                                            finally
                                            {
                                                @in.Close();
                                            }
                                        }
                                        else
                                        {
                                            entry.SetObjectId(f.EntryObjectId);
                                        }
                                        builder.Add(entry);
                                        lastAddedFile = path;
                                    }
                                    else
                                    {
                                        builder.Add(c.GetDirCacheEntry());
                                    }
                                }
                                else
                                {
                                    if (c != null && (!update || FileMode.GITLINK == c.EntryFileMode))
                                    {
                                        builder.Add(c.GetDirCacheEntry());
                                    }
                                }
                            }
                        }
                    }
                }
                inserter.Flush();
                builder.Commit();
                SetCallable(false);
            }
            catch (IOException e)
            {
                throw new JGitInternalException(JGitText.Get().exceptionCaughtDuringExecutionOfAddCommand
                                                , e);
            }
            finally
            {
                inserter.Release();
                if (dc != null)
                {
                    dc.Unlock();
                }
            }
            return(dc);
        }
Пример #11
0
        /// <exception cref="System.IO.IOException"></exception>
        private DirCache CreateTemporaryIndex(ObjectId headId, DirCache index)
        {
            ObjectInserter inserter = null;
            // get DirCacheEditor to modify the index if required
            DirCacheEditor dcEditor = index.Editor();
            // get DirCacheBuilder for newly created in-core index to build a
            // temporary index for this commit
            DirCache        inCoreIndex = DirCache.NewInCore();
            DirCacheBuilder dcBuilder   = inCoreIndex.Builder();

            onlyProcessed = new bool[only.Count];
            bool     emptyCommit = true;
            TreeWalk treeWalk    = new TreeWalk(repo);
            int      dcIdx       = treeWalk.AddTree(new DirCacheIterator(index));
            int      fIdx        = treeWalk.AddTree(new FileTreeIterator(repo));
            int      hIdx        = -1;

            if (headId != null)
            {
                hIdx = treeWalk.AddTree(new RevWalk(repo).ParseTree(headId));
            }
            treeWalk.Recursive = true;
            while (treeWalk.Next())
            {
                string path = treeWalk.PathString;
                // check if current entry's path matches a specified path
                int pos = LookupOnly(path);
                CanonicalTreeParser hTree = null;
                if (hIdx != -1)
                {
                    hTree = treeWalk.GetTree <CanonicalTreeParser>(hIdx);
                }
                if (pos >= 0)
                {
                    // include entry in commit
                    DirCacheIterator dcTree = treeWalk.GetTree <DirCacheIterator>(dcIdx);
                    FileTreeIterator fTree  = treeWalk.GetTree <FileTreeIterator>(fIdx);
                    // check if entry refers to a tracked file
                    bool tracked = dcTree != null || hTree != null;
                    if (!tracked)
                    {
                        break;
                    }
                    if (fTree != null)
                    {
                        // create a new DirCacheEntry with data retrieved from disk
                        DirCacheEntry dcEntry     = new DirCacheEntry(path);
                        long          entryLength = fTree.GetEntryLength();
                        dcEntry.SetLength(entryLength);
                        dcEntry.LastModified = fTree.GetEntryLastModified();
                        dcEntry.FileMode     = fTree.GetIndexFileMode(dcTree);
                        bool objectExists = (dcTree != null && fTree.IdEqual(dcTree)) || (hTree != null &&
                                                                                          fTree.IdEqual(hTree));
                        if (objectExists)
                        {
                            dcEntry.SetObjectId(fTree.EntryObjectId);
                        }
                        else
                        {
                            if (FileMode.GITLINK.Equals(dcEntry.FileMode))
                            {
                                dcEntry.SetObjectId(fTree.EntryObjectId);
                            }
                            else
                            {
                                // insert object
                                if (inserter == null)
                                {
                                    inserter = repo.NewObjectInserter();
                                }
                                long        contentLength = fTree.GetEntryContentLength();
                                InputStream inputStream   = fTree.OpenEntryStream();
                                try
                                {
                                    dcEntry.SetObjectId(inserter.Insert(Constants.OBJ_BLOB, contentLength, inputStream
                                                                        ));
                                }
                                finally
                                {
                                    inputStream.Close();
                                }
                            }
                        }
                        // update index
                        dcEditor.Add(new _PathEdit_375(dcEntry, path));
                        // add to temporary in-core index
                        dcBuilder.Add(dcEntry);
                        if (emptyCommit && (hTree == null || !hTree.IdEqual(fTree) || hTree.EntryRawMode
                                            != fTree.EntryRawMode))
                        {
                            // this is a change
                            emptyCommit = false;
                        }
                    }
                    else
                    {
                        // if no file exists on disk, remove entry from index and
                        // don't add it to temporary in-core index
                        dcEditor.Add(new DirCacheEditor.DeletePath(path));
                        if (emptyCommit && hTree != null)
                        {
                            // this is a change
                            emptyCommit = false;
                        }
                    }
                    // keep track of processed path
                    onlyProcessed[pos] = true;
                }
                else
                {
                    // add entries from HEAD for all other paths
                    if (hTree != null)
                    {
                        // create a new DirCacheEntry with data retrieved from HEAD
                        DirCacheEntry dcEntry = new DirCacheEntry(path);
                        dcEntry.SetObjectId(hTree.EntryObjectId);
                        dcEntry.FileMode = hTree.EntryFileMode;
                        // add to temporary in-core index
                        dcBuilder.Add(dcEntry);
                    }
                }
            }
            // there must be no unprocessed paths left at this point; otherwise an
            // untracked or unknown path has been specified
            for (int i = 0; i < onlyProcessed.Length; i++)
            {
                if (!onlyProcessed[i])
                {
                    throw new JGitInternalException(MessageFormat.Format(JGitText.Get().entryNotFoundByPath
                                                                         , only[i]));
                }
            }
            // there must be at least one change
            if (emptyCommit)
            {
                throw new JGitInternalException(JGitText.Get().emptyCommit);
            }
            // update index
            dcEditor.Commit();
            // finish temporary in-core index used for this commit
            dcBuilder.Finish();
            return(inCoreIndex);
        }
Пример #12
0
        /// <exception cref="System.IO.FileNotFoundException"></exception>
        /// <exception cref="System.InvalidOperationException"></exception>
        /// <exception cref="System.IO.IOException"></exception>
        private bool ContentMerge(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser
                                  theirs)
        {
            MergeFormatter fmt      = new MergeFormatter();
            RawText        baseText = @base == null ? RawText.EMPTY_TEXT : GetRawText(@base.EntryObjectId
                                                                                      , db);
            // do the merge
            MergeResult <RawText> result = mergeAlgorithm.Merge(RawTextComparator.DEFAULT, baseText
                                                                , GetRawText(ours.EntryObjectId, db), GetRawText(theirs.EntryObjectId, db));
            FilePath         of = null;
            FileOutputStream fos;

            if (!inCore)
            {
                FilePath workTree = db.WorkTree;
                if (workTree == null)
                {
                    // TODO: This should be handled by WorkingTreeIterators which
                    // support write operations
                    throw new NotSupportedException();
                }
                of  = new FilePath(workTree, tw.PathString);
                fos = new FileOutputStream(of);
                try
                {
                    fmt.FormatMerge(fos, result, Arrays.AsList(commitNames), Constants.CHARACTER_ENCODING
                                    );
                }
                finally
                {
                    fos.Close();
                }
            }
            else
            {
                if (!result.ContainsConflicts())
                {
                    // When working inCore, only trivial merges can be handled,
                    // so we generate objects only in conflict free cases
                    of  = FilePath.CreateTempFile("merge_", "_temp", null);
                    fos = new FileOutputStream(of);
                    try
                    {
                        fmt.FormatMerge(fos, result, Arrays.AsList(commitNames), Constants.CHARACTER_ENCODING
                                        );
                    }
                    finally
                    {
                        fos.Close();
                    }
                }
            }
            if (result.ContainsConflicts())
            {
                // a conflict occurred, the file will contain conflict markers
                // the index will be populated with the three stages and only the
                // workdir (if used) contains the halfways merged content
                Add(tw.RawPath, @base, DirCacheEntry.STAGE_1);
                Add(tw.RawPath, ours, DirCacheEntry.STAGE_2);
                Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3);
                mergeResults.Put(tw.PathString, result.Upcast());
                return(false);
            }
            else
            {
                // no conflict occurred, the file will contain fully merged content.
                // the index will be populated with the new merged version
                DirCacheEntry dce = new DirCacheEntry(tw.PathString);
                dce.FileMode     = tw.GetFileMode(0);
                dce.LastModified = of.LastModified();
                dce.SetLength((int)of.Length());
                InputStream @is = new FileInputStream(of);
                try
                {
                    dce.SetObjectId(oi.Insert(Constants.OBJ_BLOB, of.Length(), @is));
                }
                finally
                {
                    @is.Close();
                    if (inCore)
                    {
                        FileUtils.Delete(of);
                    }
                }
                builder.Add(dce);
                return(true);
            }
        }
Пример #13
0
        /// <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();
            }
        }
 public override void Apply(DirCacheEntry ent)
 {
     ent.FileMode = FileMode.REGULAR_FILE;
     ent.SetLength(f.Length());
     ent.SetObjectId(id);
 }