/// <exception cref="NGit.Errors.IncorrectObjectTypeException"></exception> /// <exception cref="System.IO.IOException"></exception> private CanonicalTreeParser ParserFor(AnyObjectId id) { CanonicalTreeParser p = new CanonicalTreeParser(); p.Reset(reader, id); return(p); }
/// <summary>Back door to quickly create a subtree iterator for any subtree.</summary> /// <remarks> /// Back door to quickly create a subtree iterator for any subtree. /// <p> /// Don't use this unless you are ObjectWalk. The method is meant to be /// called only once the current entry has been identified as a tree and its /// identity has been converted into an ObjectId. /// </remarks> /// <param name="reader">reader to load the tree data from.</param> /// <param name="id">ObjectId of the tree to open.</param> /// <returns>a new parser that walks over the current subtree.</returns> /// <exception cref="System.IO.IOException">a loose object or pack file could not be read. /// </exception> public NGit.Treewalk.CanonicalTreeParser CreateSubtreeIterator0(ObjectReader reader , AnyObjectId id) { NGit.Treewalk.CanonicalTreeParser p = new NGit.Treewalk.CanonicalTreeParser(this); p.Reset(reader, id); return(p); }
/// <summary>Reset this parser to walk through the given tree.</summary> /// <remarks>Reset this parser to walk through the given tree.</remarks> /// <param name="reader">reader to use during repository access.</param> /// <param name="id"> /// identity of the tree being parsed; used only in exception /// messages if data corruption is found. /// </param> /// <returns>the root level parser.</returns> /// <exception cref="NGit.Errors.MissingObjectException">the object supplied is not available from the repository. /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException"> /// the object supplied as an argument is not actually a tree and /// cannot be parsed as though it were a tree. /// </exception> /// <exception cref="System.IO.IOException">a loose object or pack file could not be read. /// </exception> public virtual NGit.Treewalk.CanonicalTreeParser ResetRoot(ObjectReader reader, AnyObjectId id) { NGit.Treewalk.CanonicalTreeParser p = this; while (p.parent != null) { p = (NGit.Treewalk.CanonicalTreeParser)p.parent; } p.Reset(reader, id); return(p); }
public ActionResult FileDiff(string path, string fromSha1,string toSha1) { var nGit = TM_UserData_Git.Current.NGit; Func<Repository, string, string, string, string> getDiff = (gitRepo, repoPath, fromCommitId, toCommitId) => { var fromCommit = gitRepo.Resolve(fromCommitId); var toCommit = gitRepo.Resolve(toCommitId); var outputStream = "Sharpen.dll".assembly().type("ByteArrayOutputStream").ctor(new object[0]).cast<OutputStream>(); //return "diffing from {0} to {1}".format(fromCommit, toCommit); var diffFormater = new DiffFormatter(outputStream); var pathFilter = PathFilter.Create(repoPath); diffFormater.SetRepository(gitRepo); diffFormater.SetPathFilter(pathFilter); //diffFormater.Format(refLog.GetNewId(), refLog.GetOldId()); diffFormater.Format(fromCommit, toCommit); return "result: " + outputStream.str(); }; Func<Repository, string, string, string> getFistValue = (gitRepo, commitSha1, repoPath) => { var revCommit = nGit.commit(commitSha1); var outputStream = "Sharpen.dll".assembly().type("ByteArrayOutputStream").ctor(new object[0]).cast<OutputStream>(); var diffFormater = new DiffFormatter(outputStream); var pathFilter = PathFilter.Create(repoPath); diffFormater.SetRepository(gitRepo); diffFormater.SetPathFilter(pathFilter); var revWalk = new RevWalk(gitRepo); var canonicalTreeParser = new CanonicalTreeParser(null, revWalk.GetObjectReader(), revCommit.Tree); diffFormater.Format(new EmptyTreeIterator(), canonicalTreeParser); return outputStream.str().fix_CRLF(); }; var rawDiff = fromSha1 == NGit_Consts.EMPTY_SHA1 ? getFistValue(nGit.repository(), fromSha1, path) : getDiff(nGit.repository(), path, fromSha1, toSha1); var viewFile = new View_GitFileDiff() { FilePath = path, FromSha1 = fromSha1, ToSha1 = toSha1, Diff = rawDiff }; return View(viewFile); }
/// <returns>this iterator, or its parent, if the tree is at eof.</returns> public virtual NGit.Treewalk.CanonicalTreeParser Next() { NGit.Treewalk.CanonicalTreeParser p = this; for (; ;) { if (p.nextPtr == p.raw.Length) { // This parser has reached EOF, return to the parent. if (p.parent == null) { p.currPtr = p.nextPtr; return(p); } p = (NGit.Treewalk.CanonicalTreeParser)p.parent; continue; } p.prevPtr = p.currPtr; p.currPtr = p.nextPtr; p.ParseEntry(); return(p); } }
private static bool IsValidPath(CanonicalTreeParser t) { for (CanonicalTreeParser i = t; i != null; i = i.GetParent()) { if (!IsValidPathSegment(i)) { return false; } } return true; }
/// <summary>Here the main work is done.</summary> /// <remarks> /// Here the main work is done. This method is called for each existing path /// in head, index and merge. This method decides what to do with the /// corresponding index entry: keep it, update it, remove it or mark a /// conflict. /// </remarks> /// <param name="h">the entry for the head</param> /// <param name="m">the entry for the merge</param> /// <param name="i">the entry for the index</param> /// <param name="f">the file in the working tree</param> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> internal virtual void ProcessEntry(CanonicalTreeParser h, CanonicalTreeParser m, DirCacheBuildIterator i, WorkingTreeIterator f) { DirCacheEntry dce = i != null ? i.GetDirCacheEntry() : null; string name = walk.PathString; if (m != null && !IsValidPath(m)) { throw new InvalidPathException(m.EntryPathString); } if (i == null && m == null && h == null) { // File/Directory conflict case #20 if (walk.IsDirectoryFileConflict()) { // TODO: check whether it is always correct to report a conflict here Conflict(name, null, null, null); } // file only exists in working tree -> ignore it return; } ObjectId iId = (i == null ? null : i.EntryObjectId); ObjectId mId = (m == null ? null : m.EntryObjectId); ObjectId hId = (h == null ? null : h.EntryObjectId); FileMode iMode = (i == null ? null : i.EntryFileMode); FileMode mMode = (m == null ? null : m.EntryFileMode); FileMode hMode = (h == null ? null : h.EntryFileMode); // The information whether head,index,merge iterators are currently // pointing to file/folder/non-existing is encoded into this variable. // // To decode write down ffMask in hexadecimal form. The last digit // represents the state for the merge iterator, the second last the // state for the index iterator and the third last represents the state // for the head iterator. The hexadecimal constant "F" stands for // "file", // an "D" stands for "directory" (tree), and a "0" stands for // non-existing // // Examples: // ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree // ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing int ffMask = 0; if (h != null) { ffMask = FileMode.TREE.Equals(hMode) ? unchecked((int)(0xD00)) : unchecked((int)( 0xF00)); } if (i != null) { ffMask |= FileMode.TREE.Equals(iMode) ? unchecked((int)(0x0D0)) : unchecked((int) (0x0F0)); } if (m != null) { ffMask |= FileMode.TREE.Equals(mMode) ? unchecked((int)(0x00D)) : unchecked((int) (0x00F)); } // Check whether we have a possible file/folder conflict. Therefore we // need a least one file and one folder. if (((ffMask & unchecked((int)(0x222))) != unchecked((int)(0x000))) && (((ffMask & unchecked((int)(0x00F))) == unchecked((int)(0x00D))) || ((ffMask & unchecked(( int)(0x0F0))) == unchecked((int)(0x0D0))) || ((ffMask & unchecked((int)(0xF00))) == unchecked((int)(0xD00))))) { switch (ffMask) { case unchecked((int)(0xDDF)): { // There are 3*3*3=27 possible combinations of file/folder // conflicts. Some of them are not-relevant because // they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following // switch processes all relevant cases. // 1 2 if (IsModified(name)) { Conflict(name, dce, h, m); } else { // 1 Update(name, mId, mMode); } // 2 break; } case unchecked((int)(0xDFD)): { // 3 4 // CAUTION: I put it into removed instead of updated, because // that's what our tests expect // updated.put(name, mId); Remove(name); break; } case unchecked((int)(0xF0D)): { // 18 Remove(name); break; } case unchecked((int)(0xDFF)): case unchecked((int)(0xFDD)): { // 5 6 // 10 11 // TODO: make use of tree extension as soon as available in jgit // we would like to do something like // if (!equalIdAndMode(iId, iMode, mId, mMode) // conflict(name, i.getDirCacheEntry(), h, m); // But since we don't know the id of a tree in the index we do // nothing here and wait that conflicts between index and merge // are found later break; } case unchecked((int)(0xD0F)): { // 19 Update(name, mId, mMode); break; } case unchecked((int)(0xDF0)): case unchecked((int)(0x0FD)): { // conflict without a rule // 15 Conflict(name, dce, h, m); break; } case unchecked((int)(0xFDF)): { // 7 8 9 if (EqualIdAndMode(hId, hMode, mId, mMode)) { if (IsModified(name)) { Conflict(name, dce, h, m); } else { // 8 Update(name, mId, mMode); } } else { // 7 if (!IsModified(name)) { Update(name, mId, mMode); } else { // 9 // To be confirmed - this case is not in the table. Conflict(name, dce, h, m); } } break; } case unchecked((int)(0xFD0)): { // keep without a rule Keep(dce); break; } case unchecked((int)(0xFFD)): { // 12 13 14 if (EqualIdAndMode(hId, hMode, iId, iMode)) { if (f == null || f.IsModified(dce, true)) { Conflict(name, dce, h, m); } else { Remove(name); } } else { Conflict(name, dce, h, m); } break; } case unchecked((int)(0x0DF)): { // 16 17 if (!IsModified(name)) { Update(name, mId, mMode); } else { Conflict(name, dce, h, m); } break; } default: { Keep(dce); break; } } return; } // if we have no file at all then there is nothing to do if ((ffMask & unchecked((int)(0x222))) == 0) { return; } if ((ffMask == unchecked((int)(0x00F))) && f != null && FileMode.TREE.Equals(f.EntryFileMode )) { // File/Directory conflict case #20 Conflict(name, null, h, m); } if (i == null) { // make sure not to overwrite untracked files if (f != null) { // A submodule is not a file. We should ignore it if (!FileMode.GITLINK.Equals(mMode)) { // a dirty worktree: the index is empty but we have a // workingtree-file if (mId == null || !EqualIdAndMode(mId, mMode, f.EntryObjectId, f.EntryFileMode)) { Conflict(name, null, h, m); return; } } } if (h == null) { Update(name, mId, mMode); } else { // 1 if (m == null) { Remove(name); } else { // 2 Update(name, mId, mMode); } } } else { // 3 if (h == null) { if (m == null || EqualIdAndMode(mId, mMode, iId, iMode)) { if (m == null && walk.IsDirectoryFileConflict()) { if (dce != null && (f == null || f.IsModified(dce, true))) { Conflict(name, dce, h, m); } else { Remove(name); } } else { Keep(dce); } } else { Conflict(name, dce, h, m); } } else { if (m == null) { if (iMode == FileMode.GITLINK) { // Submodules that disappear from the checkout must // be removed from the index, but not deleted from disk. Remove(name); } else { if (EqualIdAndMode(hId, hMode, iId, iMode)) { if (f == null || f.IsModified(dce, true)) { Conflict(name, dce, h, m); } else { Remove(name); } } else { Conflict(name, dce, h, m); } } } else { if (!EqualIdAndMode(hId, hMode, mId, mMode) && !EqualIdAndMode(hId, hMode, iId, iMode ) && !EqualIdAndMode(mId, mMode, iId, iMode)) { Conflict(name, dce, h, m); } else { if (EqualIdAndMode(hId, hMode, iId, iMode) && !EqualIdAndMode(mId, mMode, iId, iMode )) { // For submodules just update the index with the new SHA-1 if (dce != null && FileMode.GITLINK.Equals(dce.FileMode)) { Update(name, mId, mMode); } else { if (dce != null && (f == null || f.IsModified(dce, true))) { Conflict(name, dce, h, m); } else { Update(name, mId, mMode); } } } else { Keep(dce); } } } } } }
public override string ToString() { byte[] raw = ToByteArray(); CanonicalTreeParser p = new CanonicalTreeParser(); p.Reset(raw); StringBuilder r = new StringBuilder(); r.Append("Tree={"); if (!p.Eof) { r.Append('\n'); try { new ObjectChecker().CheckTree(raw); } catch (CorruptObjectException error) { r.Append("*** ERROR: ").Append(error.Message).Append("\n"); r.Append('\n'); } } while (!p.Eof) { FileMode mode = p.EntryFileMode; r.Append(mode); r.Append(' '); r.Append(Constants.TypeString(mode.GetObjectType())); r.Append(' '); r.Append(p.EntryObjectId.Name); r.Append(' '); r.Append(p.EntryPathString); r.Append('\n'); p.Next(); } r.Append("}"); return r.ToString(); }
private CanonicalTreeParser(NGit.Treewalk.CanonicalTreeParser p) : base(p) { }
/// <summary>Does the content merge.</summary> /// <remarks> /// Does the content merge. The three texts base, ours and theirs are /// specified with /// <see cref="NGit.Treewalk.CanonicalTreeParser">NGit.Treewalk.CanonicalTreeParser</see> /// . If any of the parsers is /// specified as <code>null</code> then an empty text will be used instead. /// </remarks> /// <param name="base"></param> /// <param name="ours"></param> /// <param name="theirs"></param> /// <returns>the result of the content merge</returns> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private MergeResult<RawText> ContentMerge(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs) { RawText baseText = @base == null ? RawText.EMPTY_TEXT : GetRawText(@base.EntryObjectId , db); RawText ourText = ours == null ? RawText.EMPTY_TEXT : GetRawText(ours.EntryObjectId , db); RawText theirsText = theirs == null ? RawText.EMPTY_TEXT : GetRawText(theirs.EntryObjectId , db); return (mergeAlgorithm.Merge(RawTextComparator.DEFAULT, baseText, ourText, theirsText )); }
/// <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); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); 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(oi.Insert(Constants.OBJ_BLOB, of.Length(), @is)); } finally { @is.Close(); if (inCore) { FileUtils.Delete(of); } } builder.Add(dce); } }
protected internal override void Reset(int retainFlags) { base.Reset(retainFlags); foreach (RevObject obj in rootObjects) { obj.flags &= ~IN_PENDING; } rootObjects = new AList<RevObject>(); pendingObjects = new BlockObjQueue(); treeWalk = new CanonicalTreeParser(); currentTree = null; last = null; firstCommit = null; lastCommit = null; }
/// <exception cref="NGit.Errors.MissingObjectException"></exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException"></exception> /// <exception cref="System.IO.IOException"></exception> private void MarkTreeUninteresting(RevTree tree) { if ((tree.flags & UNINTERESTING) != 0) { return; } tree.flags |= UNINTERESTING; treeWalk = treeWalk.ResetRoot(reader, tree); while (!treeWalk.Eof) { FileMode mode = treeWalk.EntryFileMode; int sType = mode.GetObjectType(); switch (sType) { case Constants.OBJ_BLOB: { treeWalk.GetEntryObjectId(idBuffer); LookupBlob(idBuffer).flags |= UNINTERESTING; break; } case Constants.OBJ_TREE: { treeWalk.GetEntryObjectId(idBuffer); RevTree t = LookupTree(idBuffer); if ((t.flags & UNINTERESTING) == 0) { t.flags |= UNINTERESTING; treeWalk = treeWalk.CreateSubtreeIterator0(reader, t); continue; } break; } default: { if (FileMode.GITLINK.Equals(mode)) { break; } treeWalk.GetEntryObjectId(idBuffer); throw new CorruptObjectException(MessageFormat.Format(JGitText.Get().corruptObjectInvalidMode3 , mode, idBuffer.Name, treeWalk.EntryPathString, tree)); } } treeWalk = treeWalk.Next(); } }
/// <summary>Pop the next most recent object.</summary> /// <remarks>Pop the next most recent object.</remarks> /// <returns>next most recent object; null if traversal is over.</returns> /// <exception cref="NGit.Errors.MissingObjectException"> /// one or or more of the next objects are not available from the /// object database, but were thought to be candidates for /// traversal. This usually indicates a broken link. /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException"> /// one or or more of the objects in a tree do not match the type /// indicated. /// </exception> /// <exception cref="System.IO.IOException">a pack file or loose object could not be read. /// </exception> public virtual RevObject NextObject() { if (last != null) { treeWalk = last is RevTree ? Enter(last) : treeWalk.Next(); } while (!treeWalk.Eof) { FileMode mode = treeWalk.EntryFileMode; switch (mode.GetObjectType()) { case Constants.OBJ_BLOB: { treeWalk.GetEntryObjectId(idBuffer); RevBlob o = LookupBlob(idBuffer); if ((o.flags & SEEN) != 0) { break; } o.flags |= SEEN; if (ShouldSkipObject(o)) { break; } last = o; return o; } case Constants.OBJ_TREE: { treeWalk.GetEntryObjectId(idBuffer); RevTree o = LookupTree(idBuffer); if ((o.flags & SEEN) != 0) { break; } o.flags |= SEEN; if (ShouldSkipObject(o)) { break; } last = o; return o; } default: { if (FileMode.GITLINK.Equals(mode)) { break; } treeWalk.GetEntryObjectId(idBuffer); throw new CorruptObjectException(MessageFormat.Format(JGitText.Get().corruptObjectInvalidMode3 , mode, idBuffer.Name, treeWalk.EntryPathString, currentTree.Name)); } } treeWalk = treeWalk.Next(); } if (firstCommit != null) { reader.WalkAdviceBeginTrees(this, firstCommit, lastCommit); firstCommit = null; lastCommit = null; } last = null; for (; ; ) { RevObject o = pendingObjects.Next(); if (o == null) { reader.WalkAdviceEnd(); return null; } if ((o.flags & SEEN) != 0) { continue; } o.flags |= SEEN; if (ShouldSkipObject(o)) { continue; } if (o is RevTree) { currentTree = (RevTree)o; treeWalk = treeWalk.ResetRoot(reader, currentTree); } return o; } }
public override void Dispose() { base.Dispose(); pendingObjects = new BlockObjQueue(); treeWalk = new CanonicalTreeParser(); currentTree = null; last = null; firstCommit = null; lastCommit = null; }
/// <summary>Create a new revision and object walker for a given repository.</summary> /// <remarks>Create a new revision and object walker for a given repository.</remarks> /// <param name="or"> /// the reader the walker will obtain data from. The reader should /// be released by the caller when the walker is no longer /// required. /// </param> public ObjectWalk(ObjectReader or) : base(or) { rootObjects = new AList<RevObject>(); pendingObjects = new BlockObjQueue(); treeWalk = new CanonicalTreeParser(); }
/// <exception cref="System.IO.IOException"></exception> private AbstractTreeIterator GetTreeIterator(string name) { ObjectId id = db.Resolve(name); if (id == null) { throw new ArgumentException(name); } CanonicalTreeParser p = new CanonicalTreeParser(); ObjectReader or = db.NewObjectReader(); try { p.Reset(or, new RevWalk(db).ParseTree(id)); return p; } finally { or.Release(); } }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occurred /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); int modeB = tw.GetRawMode(T_BASE); if (modeO == 0 && modeT == 0 && modeB == 0) { // File is either untracked or new, staged but uncommitted return true; } if (IsIndexDirty()) { return false; } if (NonTree(modeO) && modeO == modeT && tw.IdEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS are equal: it doesn't matter which one we choose. // OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return true; } if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return true; } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to BASE. All changes must be in // THEIRS. THEIRS is chosen. // Check worktree before checking out THEIRS if (IsWorktreeDirty()) { return false; } if (NonTree(modeT)) { DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return true; } else { if (modeT == 0 && modeB != 0) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeCheckedOut.Put(tw.PathString, null); return true; } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return true; } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { // Check worktree before modifying files if (IsWorktreeDirty()) { return false; } if (!ContentMerge(@base, ours, theirs)) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } return true; }
private static bool IsValidPathSegment(CanonicalTreeParser t) { string osName = SystemReader.GetInstance().GetProperty("os.name"); bool isWindows = "Windows".Equals(osName); bool isOSX = "Darwin".Equals(osName) || "Mac OS X".Equals(osName); bool ignCase = isOSX || isWindows; int ptr = t.GetNameOffset(); byte[] raw = t.GetEntryPathBuffer(); int end = ptr + t.NameLength; // Validate path component at this level of the tree int start = ptr; while (ptr < end) { if (raw[ptr] == '/') { return false; } if (isWindows) { if (raw[ptr] == '\\') { return false; } if (raw[ptr] == ':') { return false; } } ptr++; } // '.' and '.'' are invalid here if (ptr - start == 1) { if (raw[start] == '.') { return false; } } else { if (ptr - start == 2) { if (raw[start] == '.') { if (raw[start + 1] == '.') { return false; } } } else { if (ptr - start == 4) { // .git (possibly case insensitive) is disallowed if (raw[start] == '.') { if (raw[start + 1] == 'g' || (ignCase && raw[start + 1] == 'G')) { if (raw[start + 2] == 'i' || (ignCase && raw[start + 2] == 'I')) { if (raw[start + 3] == 't' || (ignCase && raw[start + 3] == 'T')) { return false; } } } } } } } if (isWindows) { // Space or period at end of file name is ignored by Windows. // Treat this as a bad path for now. We may want to handle // this as case insensitivity in the future. if (raw[ptr - 1] == '.' || raw[ptr - 1] == ' ') { return false; } int i; // Bad names, eliminate suffix first for (i = start; i < ptr; ++i) { if (raw[i] == '.') { break; } } int len = i - start; if (len == 3 || len == 4) { for (int j = 0; j < forbidden.Length; ++j) { if (forbidden[j].Length == len) { if (((sbyte)ToUpper(raw[start])) < forbidden[j][0]) { break; } int k; for (k = 0; k < len; ++k) { if (ToUpper(raw[start + k]) != forbidden[j][k]) { break; } } if (k == len) { return false; } } } } } return true; }
/// <summary> /// Processing an entry in the context of /// <see cref="PrescanOneTree()">PrescanOneTree()</see> /// when only /// one tree is given /// </summary> /// <param name="m">the tree to merge</param> /// <param name="i">the index</param> /// <param name="f">the working tree</param> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i , WorkingTreeIterator f) { if (m != null) { // There is an entry in the merge commit. Means: we want to update // what's currently in the index and working-tree to that one if (i == null) { // The index entry is missing if (f != null && !FileMode.TREE.Equals(f.EntryFileMode) && !f.IsEntryIgnored()) { // don't overwrite an untracked and not ignored file conflicts.AddItem(walk.PathString); } else { Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } } else { if (f == null || !m.IdEqual(i)) { // The working tree file is missing or the merge content differs // from index content Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } else { if (i.GetDirCacheEntry() != null) { // The index contains a file (and not a folder) if (f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0) { // The working tree file is dirty or the index contains a // conflict Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } else { Keep(i.GetDirCacheEntry()); } } else { // The index contains a folder Keep(i.GetDirCacheEntry()); } } } } else { // There is no entry in the merge commit. Means: we want to delete // what's currently in the index and working tree if (f != null) { // There is a file/folder for that path in the working tree if (walk.IsDirectoryFileConflict()) { conflicts.AddItem(walk.PathString); } else { // No file/folder conflict exists. All entries are files or // all entries are folders if (i != null) { // ... and the working tree contained a file or folder // -> add it to the removed set and remove it from // conflicts set Remove(i.EntryPathString); conflicts.Remove(i.EntryPathString); } } } else { // untracked file, neither contained in tree to merge // nor in index // There is no file/folder for that path in the working tree. // The only entry we have is the index entry. If that entry is a // conflict simply remove it. Otherwise keep that entry in the // index if (i.GetDirCacheEntry().Stage == 0) { Keep(i.GetDirCacheEntry()); } } } }
internal BaseSearch(ProgressMonitor countingMonitor, ICollection<RevTree> bases, ObjectIdOwnerMap<ObjectToPack> objects, IList<ObjectToPack> edges, ObjectReader or) { progress = countingMonitor; reader = or; baseTrees = Sharpen.Collections.ToArray(bases, new ObjectId[bases.Count]); objectsMap = objects; edgeObjects = edges; alreadyProcessed = new IntSet(); treeCache = new ObjectIdOwnerMap<BaseSearch.TreeWithData>(); parser = new CanonicalTreeParser(); idBuf = new MutableObjectId(); }
/// <summary> /// Processing an entry in the context of /// <see cref="PrescanOneTree()">PrescanOneTree()</see> /// when only /// one tree is given /// </summary> /// <param name="m">the tree to merge</param> /// <param name="i">the index</param> /// <param name="f">the working tree</param> internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i , WorkingTreeIterator f) { if (m != null) { if (i == null || f == null || !m.IdEqual(i) || (i.GetDirCacheEntry() != null && ( f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0))) { Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } else { Keep(i.GetDirCacheEntry()); } } else { if (f != null) { if (walk.IsDirectoryFileConflict()) { conflicts.AddItem(walk.PathString); } else { if (i != null) { // ... and the working dir contained a file or folder -> // add it to the removed set and remove it from // conflicts set Remove(i.EntryPathString); conflicts.Remove(i.EntryPathString); } } } else { if (i.GetDirCacheEntry().Stage == 0) { Keep(i.GetDirCacheEntry()); } } } }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occurred /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); int modeB = tw.GetRawMode(T_BASE); if (modeO == 0 && modeT == 0 && modeB == 0) { // File is either untracked or new, staged but uncommitted return true; } if (IsIndexDirty()) { return false; } if (NonTree(modeO) && NonTree(modeT) && tw.IdEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS have equal content. Check the file mode if (modeO == modeT) { // content and mode of OURS and THEIRS are equal: it doesn't // matter which one we choose. OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return true; } else { // same content but different mode on OURS and THEIRS. // Try to merge the mode and report an error if this is // not possible. int newMode = MergeFileModes(modeB, modeO, modeT); if (newMode != FileMode.MISSING.GetBits()) { if (newMode == modeO) { // ours version is preferred Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); } else { // the preferred version THEIRS has a different mode // than ours. Check it out! if (IsWorktreeDirty()) { return false; } DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); toBeCheckedOut.Put(tw.PathString, e); } return true; } else { // FileModes are not mergeable. We found a conflict on modes Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); mergeResults.Put(tw.PathString, new MergeResult<RawText>(Sharpen.Collections.EmptyList <RawText>()).Upcast ()); } return true; } } if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return true; } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to BASE. All changes must be in // THEIRS. THEIRS is chosen. // Check worktree before checking out THEIRS if (IsWorktreeDirty()) { return false; } if (NonTree(modeT)) { DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return true; } else { if (modeT == 0 && modeB != 0) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeCheckedOut.Put(tw.PathString, null); return true; } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return true; } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { // Check worktree before modifying files if (IsWorktreeDirty()) { return false; } MergeResult<RawText> result = ContentMerge(@base, ours, theirs); FilePath of = WriteMergedFile(result); UpdateIndex(@base, ours, theirs, result, of); if (result.ContainsConflicts()) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } else { if (modeO != modeT) { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.IdEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw.IdEqual(T_BASE , T_THEIRS)))) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); // OURS was deleted checkout THEIRS if (modeO == 0) { // Check worktree before checking out THEIRS if (IsWorktreeDirty()) { return false; } if (NonTree(modeT)) { if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } } } unmergedPaths.AddItem(tw.PathString); // generate a MergeResult for the deleted file mergeResults.Put(tw.PathString, ContentMerge(@base, ours, theirs).Upcast ()); } } } return true; }
/// <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> /// <returns>the entry which was added to the index</returns> private DirCacheEntry Add(byte[] path, CanonicalTreeParser p, int stage) { if (p != null && !p.EntryFileMode.Equals(FileMode.TREE)) { DirCacheEntry e = new DirCacheEntry(path, stage); e.FileMode = p.EntryFileMode; e.SetObjectId(p.EntryObjectId); builder.Add(e); return e; } return null; }
public virtual void TestTreeIteratorWithGitmodules() { ObjectId subId = ObjectId.FromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); string path = "sub"; Config gitmodules = new Config(); gitmodules.SetString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, ConfigConstants .CONFIG_KEY_PATH, "sub"); gitmodules.SetString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path, ConfigConstants .CONFIG_KEY_URL, "git://example.com/sub"); RevCommit commit = testDb.GetRevWalk().ParseCommit(testDb.Commit().NoParents().Add (Constants.DOT_GIT_MODULES, gitmodules.ToText()).Edit(new _PathEdit_397(subId, path )).Create()); CanonicalTreeParser p = new CanonicalTreeParser(); p.Reset(testDb.GetRevWalk().GetObjectReader(), commit.Tree); SubmoduleWalk gen = SubmoduleWalk.ForPath(db, p, "sub"); NUnit.Framework.Assert.AreEqual(path, gen.GetPath()); NUnit.Framework.Assert.AreEqual(subId, gen.GetObjectId()); NUnit.Framework.Assert.AreEqual(new FilePath(db.WorkTree, path), gen.GetDirectory ()); NUnit.Framework.Assert.IsNull(gen.GetConfigUpdate()); NUnit.Framework.Assert.IsNull(gen.GetConfigUrl()); NUnit.Framework.Assert.AreEqual("sub", gen.GetModulesPath()); NUnit.Framework.Assert.IsNull(gen.GetModulesUpdate()); NUnit.Framework.Assert.AreEqual("git://example.com/sub", gen.GetModulesUrl()); NUnit.Framework.Assert.IsNull(gen.GetRepository()); NUnit.Framework.Assert.IsFalse(gen.Next()); }
/// <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 occured, 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 occured, 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; } }
/// <summary>Create a new revision and object walker for a given repository.</summary> /// <remarks>Create a new revision and object walker for a given repository.</remarks> /// <param name="or"> /// the reader the walker will obtain data from. The reader should /// be released by the caller when the walker is no longer /// required. /// </param> public ObjectWalk(ObjectReader or) : base(or) { pendingObjects = new BlockObjQueue(); treeWalk = new CanonicalTreeParser(); }
/// <summary> /// Set the tree used by this walk for finding /// <code>.gitmodules</code> /// . /// <p> /// The root tree is not read until the first submodule is encountered by the /// walk. /// <p> /// This method need only be called if constructing a walk manually instead of /// with one of the static factory methods above. /// </summary> /// <param name="id">ID of a tree containing .gitmodules</param> /// <returns>this generator</returns> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> public virtual NGit.Submodule.SubmoduleWalk SetRootTree(AnyObjectId id) { CanonicalTreeParser p = new CanonicalTreeParser(); p.Reset(walk.ObjectReader, id); rootTree = p; modulesConfig = null; return this; }
public static IEnumerable<DiffEntry> CompareCommits (NGit.Repository repo, AnyObjectId reference, ObjectId compared) { var diff = new MyersDiff (repo); var firstTree = new CanonicalTreeParser (); firstTree.Reset (repo.NewObjectReader (), new RevWalk (repo).ParseTree (reference)); diff.SetNewTree (firstTree); if (compared != ObjectId.ZeroId) { var secondTree = new CanonicalTreeParser (); secondTree.Reset (repo.NewObjectReader (), new RevWalk (repo).ParseTree (compared)); if (compared != ObjectId.ZeroId) diff.SetOldTree (secondTree); } return diff.Call (); }
protected internal override void Reset(int retainFlags) { base.Reset(retainFlags); pendingObjects = new BlockObjQueue(); treeWalk = new CanonicalTreeParser(); currentTree = null; last = null; firstCommit = null; lastCommit = null; }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occured /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeI = tw.GetRawMode(T_INDEX); // Each index entry has to match ours, means: it has to be clean if (NonTree(modeI) && !(tw.IdEqual(T_INDEX, T_OURS) && modeO == modeI)) { failingPaths.Put(tw.PathString, ResolveMerger.MergeFailureReason.DIRTY_INDEX); return false; } int modeT = tw.GetRawMode(T_THEIRS); if (NonTree(modeO) && modeO == modeT && tw.IdEqual(T_OURS, T_THEIRS)) { // ours and theirs are equal: it doesn'nt matter // which one we choose. OURS is choosen here. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); // no checkout needed! return true; } int modeB = tw.GetRawMode(T_BASE); if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to base. All changes must be in // OURS. Choose OURS. Add(tw.RawPath, ours, DirCacheEntry.STAGE_0); return true; } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to base. All changes must be in // THEIRS. Choose THEIRS. if (NonTree(modeT)) { DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return true; } else { if ((modeT == 0) && (modeB != 0)) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeCheckedOut.Put(tw.PathString, null); return true; } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return true; } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { if (!inCore) { // We are going to update the worktree. Make sure the worktree // is not modified if (work != null && (!NonTree(work.EntryRawMode) || work.IsModified(index.GetDirCacheEntry (), true))) { failingPaths.Put(tw.PathString, ResolveMerger.MergeFailureReason.DIRTY_WORKTREE); return false; } } if (!ContentMerge(@base, ours, theirs)) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } return true; }
/// <summary> /// Processing an entry in the context of /// <see cref="PrescanOneTree()">PrescanOneTree()</see> /// when only /// one tree is given /// </summary> /// <param name="m">the tree to merge</param> /// <param name="i">the index</param> /// <param name="f">the working tree</param> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> internal virtual void ProcessEntry(CanonicalTreeParser m, DirCacheBuildIterator i , WorkingTreeIterator f) { if (m != null) { if (!IsValidPath(m)) { throw new InvalidPathException(m.EntryPathString); } // There is an entry in the merge commit. Means: we want to update // what's currently in the index and working-tree to that one if (i == null) { // The index entry is missing if (f != null && !FileMode.TREE.Equals(f.EntryFileMode) && !f.IsEntryIgnored()) { // don't overwrite an untracked and not ignored file conflicts.AddItem(walk.PathString); } else { Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } } else { if (f == null || !m.IdEqual(i)) { // The working tree file is missing or the merge content differs // from index content Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } else { if (i.GetDirCacheEntry() != null) { // The index contains a file (and not a folder) if (f.IsModified(i.GetDirCacheEntry(), true) || i.GetDirCacheEntry().Stage != 0) { // The working tree file is dirty or the index contains a // conflict Update(m.EntryPathString, m.EntryObjectId, m.EntryFileMode); } else { // update the timestamp of the index with the one from the // file if not set, as we are sure to be in sync here. DirCacheEntry entry = i.GetDirCacheEntry(); if (entry.LastModified == 0) { entry.LastModified = f.GetEntryLastModified(); } Keep(entry); } } else { // The index contains a folder Keep(i.GetDirCacheEntry()); } } } } else { // There is no entry in the merge commit. Means: we want to delete // what's currently in the index and working tree if (f != null) { // There is a file/folder for that path in the working tree if (walk.IsDirectoryFileConflict()) { conflicts.AddItem(walk.PathString); } else { // No file/folder conflict exists. All entries are files or // all entries are folders if (i != null) { // ... and the working tree contained a file or folder // -> add it to the removed set and remove it from // conflicts set Remove(i.EntryPathString); conflicts.Remove(i.EntryPathString); } } } } }
/// <summary>Back door to quickly create a subtree iterator for any subtree.</summary> /// <remarks> /// Back door to quickly create a subtree iterator for any subtree. /// <p/> /// Don't use this unless you are ObjectWalk. The method is meant to be /// called only once the current entry has been identified as a tree and its /// identity has been converted into an ObjectId. /// </remarks> /// <param name="reader">reader to load the tree data from.</param> /// <param name="id">ObjectId of the tree to open.</param> /// <returns>a new parser that walks over the current subtree.</returns> /// <exception cref="System.IO.IOException">a loose object or pack file could not be read. /// </exception> public NGit.Treewalk.CanonicalTreeParser CreateSubtreeIterator0(ObjectReader reader , AnyObjectId id) { NGit.Treewalk.CanonicalTreeParser p = new NGit.Treewalk.CanonicalTreeParser(this); p.Reset(reader, id); return p; }
/// <summary>Processes one path and tries to merge.</summary> /// <remarks> /// Processes one path and tries to merge. This method will do all do all /// trivial (not content) merges and will also detect if a merge will fail. /// The merge will fail when one of the following is true /// <ul> /// <li>the index entry does not match the entry in ours. When merging one /// branch into the current HEAD, ours will point to HEAD and theirs will /// point to the other branch. It is assumed that the index matches the HEAD /// because it will only not match HEAD if it was populated before the merge /// operation. But the merge commit should not accidentally contain /// modifications done before the merge. Check the <a href= /// "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" /// >git read-tree</a> documentation for further explanations.</li> /// <li>A conflict was detected and the working-tree file is dirty. When a /// conflict is detected the content-merge algorithm will try to write a /// merged version into the working-tree. If the file is dirty we would /// override unsaved data.</li> /// </remarks> /// <param name="base">the common base for ours and theirs</param> /// <param name="ours"> /// the ours side of the merge. When merging a branch into the /// HEAD ours will point to HEAD /// </param> /// <param name="theirs"> /// the theirs side of the merge. When merging a branch into the /// current HEAD theirs will point to the branch which is merged /// into HEAD. /// </param> /// <param name="index">the index entry</param> /// <param name="work">the file in the working tree</param> /// <returns> /// <code>false</code> if the merge will fail because the index entry /// didn't match ours or the working-dir file was dirty and a /// conflict occurred /// </returns> /// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException /// </exception> /// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException /// </exception> /// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException /// </exception> /// <exception cref="System.IO.IOException">System.IO.IOException</exception> private bool ProcessEntry(CanonicalTreeParser @base, CanonicalTreeParser ours, CanonicalTreeParser theirs, DirCacheBuildIterator index, WorkingTreeIterator work) { enterSubtree = true; int modeO = tw.GetRawMode(T_OURS); int modeT = tw.GetRawMode(T_THEIRS); int modeB = tw.GetRawMode(T_BASE); if (modeO == 0 && modeT == 0 && modeB == 0) { // File is either untracked or new, staged but uncommitted return true; } if (IsIndexDirty()) { return false; } DirCacheEntry ourDce = null; if (index == null || index.GetDirCacheEntry() == null) { // create a fake DCE, but only if ours is valid. ours is kept only // in case it is valid, so a null ourDce is ok in all other cases. if (NonTree(modeO)) { ourDce = new DirCacheEntry(tw.RawPath); ourDce.SetObjectId(tw.GetObjectId(T_OURS)); ourDce.FileMode = tw.GetFileMode(T_OURS); } } else { ourDce = index.GetDirCacheEntry(); } if (NonTree(modeO) && NonTree(modeT) && tw.IdEqual(T_OURS, T_THEIRS)) { // OURS and THEIRS have equal content. Check the file mode if (modeO == modeT) { // content and mode of OURS and THEIRS are equal: it doesn't // matter which one we choose. OURS is chosen. Since the index // is clean (the index matches already OURS) we can keep the existing one Keep(ourDce); // no checkout needed! return true; } else { // same content but different mode on OURS and THEIRS. // Try to merge the mode and report an error if this is // not possible. int newMode = MergeFileModes(modeB, modeO, modeT); if (newMode != FileMode.MISSING.GetBits()) { if (newMode == modeO) { // ours version is preferred Keep(ourDce); } else { // the preferred version THEIRS has a different mode // than ours. Check it out! if (IsWorktreeDirty(work)) { return false; } // we know about length and lastMod only after we have written the new content. // This will happen later. Set these values to 0 for know. DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0, 0, 0); toBeCheckedOut.Put(tw.PathString, e); } return true; } else { // FileModes are not mergeable. We found a conflict on modes. // For conflicting entries we don't know lastModified and length. 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); unmergedPaths.AddItem(tw.PathString); mergeResults.Put(tw.PathString, new MergeResult<RawText>(Sharpen.Collections.EmptyList <RawText>()).Upcast ()); } return true; } } if (NonTree(modeO) && modeB == modeT && tw.IdEqual(T_BASE, T_THEIRS)) { // THEIRS was not changed compared to BASE. All changes must be in // OURS. OURS is chosen. We can keep the existing entry. Keep(ourDce); // no checkout needed! return true; } if (modeB == modeO && tw.IdEqual(T_BASE, T_OURS)) { // OURS was not changed compared to BASE. All changes must be in // THEIRS. THEIRS is chosen. // Check worktree before checking out THEIRS if (IsWorktreeDirty(work)) { return false; } if (NonTree(modeT)) { // we know about length and lastMod only after we have written // the new content. // This will happen later. Set these values to 0 for know. DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_0, 0, 0); if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } return true; } else { if (modeT == 0 && modeB != 0) { // we want THEIRS ... but THEIRS contains the deletion of the // file toBeDeleted.AddItem(tw.PathString); return true; } } } if (tw.IsSubtree) { // file/folder conflicts: here I want to detect only file/folder // conflict between ours and theirs. file/folder conflicts between // base/index/workingTree and something else are not relevant or // detected later if (NonTree(modeO) && !NonTree(modeT)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); } Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } if (NonTree(modeT) && !NonTree(modeO)) { if (NonTree(modeB)) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); } Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); unmergedPaths.AddItem(tw.PathString); enterSubtree = false; return true; } // ours and theirs are both folders or both files (and treewalk // tells us we are in a subtree because of index or working-dir). // If they are both folders no content-merge is required - we can // return here. if (!NonTree(modeO)) { return true; } } // ours and theirs are both files, just fall out of the if block // and do the content merge if (NonTree(modeO) && NonTree(modeT)) { // Check worktree before modifying files if (IsWorktreeDirty(work)) { return false; } // Don't attempt to resolve submodule link conflicts if (IsGitLink(modeO) || IsGitLink(modeT)) { 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); unmergedPaths.AddItem(tw.PathString); return true; } MergeResult<RawText> result = ContentMerge(@base, ours, theirs); FilePath of = WriteMergedFile(result); UpdateIndex(@base, ours, theirs, result, of); if (result.ContainsConflicts()) { unmergedPaths.AddItem(tw.PathString); } modifiedFiles.AddItem(tw.PathString); } else { if (modeO != modeT) { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.IdEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw.IdEqual(T_BASE , T_THEIRS)))) { Add(tw.RawPath, @base, DirCacheEntry.STAGE_1, 0, 0); Add(tw.RawPath, ours, DirCacheEntry.STAGE_2, 0, 0); DirCacheEntry e = Add(tw.RawPath, theirs, DirCacheEntry.STAGE_3, 0, 0); // OURS was deleted checkout THEIRS if (modeO == 0) { // Check worktree before checking out THEIRS if (IsWorktreeDirty(work)) { return false; } if (NonTree(modeT)) { if (e != null) { toBeCheckedOut.Put(tw.PathString, e); } } } unmergedPaths.AddItem(tw.PathString); // generate a MergeResult for the deleted file mergeResults.Put(tw.PathString, ContentMerge(@base, ours, theirs).Upcast ()); } } } return true; }