public void testNoPostOrder() throws Exception { final DirCache tree = db.readDirCache(); { final DirCacheBuilder b = tree.builder(); b.add(makeFile("a")); b.add(makeFile("b/c")); b.add(makeFile("b/d")); b.add(makeFile("q")); b.finish(); assertEquals(4, tree.getEntryCount()); } final TreeWalk tw = new TreeWalk(db); tw.reset(); tw.setPostOrderTraversal(false); tw.addTree(new DirCacheIterator(tree)); assertModes("a", REGULAR_FILE, tw); assertModes("b", TREE, tw); assertTrue(tw.isSubtree()); assertFalse(tw.isPostChildren()); tw.enterSubtree(); assertModes("b/c", REGULAR_FILE, tw); assertModes("b/d", REGULAR_FILE, tw); assertModes("q", REGULAR_FILE, tw); }
@Test public void testPathsResetWithUnmerged() throws Exception { setupRepository(); String file = "a.txt"; writeTrashFile(file, "data"); git.add().addFilepattern(file).call(); git.commit().setMessage("commit").call(); DirCache index = db.lockDirCache(); DirCacheBuilder builder = index.builder(); builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, "")); builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, "")); builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, "")); builder.add(createEntry("b.txt", FileMode.REGULAR_FILE)); assertTrue(builder.commit()); assertEquals( "[a.txt, mode:100644, stage:1]" + "[a.txt, mode:100644, stage:2]" + "[a.txt, mode:100644, stage:3]" + "[b.txt, mode:100644]", indexState(0)); git.reset().addPath(file).call(); assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]", indexState(0)); }
/** * Creates an in-memory index of the ticket change. * * @param changeId * @param change * @return an in-memory index * @throws IOException */ private DirCache createIndex(Repository db, long ticketId, Change change) throws IOException, ClassNotFoundException, NoSuchFieldException { String ticketPath = toTicketPath(ticketId); DirCache newIndex = DirCache.newInCore(); DirCacheBuilder builder = newIndex.builder(); Set<String> ignorePaths = new TreeSet<String>(); try (ObjectInserter inserter = db.newObjectInserter()) { // create/update the journal // exclude the attachment content List<Change> changes = getJournal(db, ticketId); changes.add(change); String journal = TicketSerializer.serializeJournal(changes).trim(); byte[] journalBytes = journal.getBytes(Constants.ENCODING); String journalPath = ticketPath + "/" + JOURNAL; final DirCacheEntry journalEntry = new DirCacheEntry(journalPath); journalEntry.setLength(journalBytes.length); journalEntry.setLastModified(change.date.getTime()); journalEntry.setFileMode(FileMode.REGULAR_FILE); journalEntry.setObjectId( inserter.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, journalBytes)); // add journal to index builder.add(journalEntry); ignorePaths.add(journalEntry.getPathString()); // Add any attachments to the index if (change.hasAttachments()) { for (Attachment attachment : change.attachments) { // build a path name for the attachment and mark as ignored String path = toAttachmentPath(ticketId, attachment.name); ignorePaths.add(path); // create an index entry for this attachment final DirCacheEntry entry = new DirCacheEntry(path); entry.setLength(attachment.content.length); entry.setLastModified(change.date.getTime()); entry.setFileMode(FileMode.REGULAR_FILE); // insert object entry.setObjectId( inserter.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, attachment.content)); // add to temporary in-core index builder.add(entry); } } for (DirCacheEntry entry : getTreeEntries(db, ignorePaths)) { builder.add(entry); } // finish the index builder.finish(); } return newIndex; }
CommitBuilder(CommitBuilder prior) throws Exception { branch = prior.branch; DirCacheBuilder b = tree.builder(); for (int i = 0; i < prior.tree.getEntryCount(); i++) b.add(prior.tree.getEntry(i)); b.finish(); parents.add(prior.create()); }
public CommitBuilder parent(RevCommit p) throws Exception { if (parents.isEmpty()) { DirCacheBuilder b = tree.builder(); parseBody(p); b.addTree(new byte[0], DirCacheEntry.STAGE_0, pool.getObjectReader(), p.getTree()); b.finish(); } parents.add(p); return this; }
protected DirCache readTree(RevTree tree) throws IOException, MissingObjectException, IncorrectObjectTypeException { DirCache dc = DirCache.newInCore(); if (tree != null) { DirCacheBuilder b = dc.builder(); b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, tree); b.finish(); } return dc; }
/** * The resolve conflict way of three way merging * * @param baseTree * @param headTree * @param mergeTree * @param ignoreConflicts Controls what to do in case a content-merge is done and a conflict is * detected. The default setting for this should be <code>false</code>. In this case the * working tree file is filled with new content (containing conflict markers) and the index is * filled with multiple stages containing BASE, OURS and THEIRS content. Having such non-0 * stages is the sign to git tools that there are still conflicts for that path. * <p>If <code>true</code> is specified the behavior is different. In case a conflict is * detected the working tree file is again filled with new content (containing conflict * markers). But also stage 0 of the index is filled with that content. No other stages are * filled. Means: there is no conflict on that path but the new content (including conflict * markers) is stored as successful merge result. This is needed in the context of {@link * RecursiveMerger} where when determining merge bases we don't want to deal with * content-merge conflicts. * @return whether the trees merged cleanly * @throws IOException * @since 3.5 */ protected boolean mergeTrees( AbstractTreeIterator baseTree, RevTree headTree, RevTree mergeTree, boolean ignoreConflicts) throws IOException { builder = dircache.builder(); DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder); tw = new NameConflictTreeWalk(reader); tw.addTree(baseTree); tw.addTree(headTree); tw.addTree(mergeTree); tw.addTree(buildIt); if (workingTreeIterator != null) { tw.addTree(workingTreeIterator); } else { tw.setFilter(TreeFilter.ANY_DIFF); } if (!mergeTreeWalk(tw, ignoreConflicts)) { return false; } if (!inCore) { // No problem found. The only thing left to be done is to // checkout all files from "theirs" which have been selected to // go into the new index. checkout(); // All content-merges are successfully done. If we can now write the // new index we are on quite safe ground. Even if the checkout of // files coming from "theirs" fails the user can work around such // failures by checking out the index again. if (!builder.commit()) { cleanUp(); throw new IndexWriteException(); } builder = null; } else { builder.finish(); builder = null; } if (getUnmergedPaths().isEmpty() && !failed()) { resultTree = dircache.writeTree(getObjectInserter()); return true; } else { resultTree = null; return false; } }
private static DirCache readTree(final Repository pdb, final Ref branch) throws MissingObjectException, IncorrectObjectTypeException, IOException { try (RevWalk rw = new RevWalk(pdb)) { final DirCache dc = DirCache.newInCore(); final DirCacheBuilder b = dc.builder(); b.addTree( new byte[0], // no prefix path DirCacheEntry.STAGE_0, // standard stage pdb.newObjectReader(), rw.parseTree(branch.getObjectId())); b.finish(); return dc; } }
/** * Construct a tree from a specific listing of file entries. * * @param entries the files to include in the tree. The collection does not need to be sorted * properly and may be empty. * @return reference to the tree specified by the entry list. * @throws Exception */ public RevTree tree(final DirCacheEntry... entries) throws Exception { final DirCache dc = DirCache.newInCore(); final DirCacheBuilder b = dc.builder(); for (final DirCacheEntry e : entries) b.add(e); b.finish(); ObjectId root; try { root = dc.writeTree(inserter); inserter.flush(); } finally { inserter.release(); } return pool.lookupTree(root); }
@Test public void testEntriesWithin() throws Exception { final DirCache dc = db.readDirCache(); final String[] paths = {"a.", "a/b", "a/c", "a/d", "a0b"}; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(FileMode.REGULAR_FILE); } final int aFirst = 1; final int aLast = 3; final DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.length; i++) b.add(ents[i]); b.finish(); assertEquals(paths.length, dc.getEntryCount()); for (int i = 0; i < ents.length; i++) assertSame(ents[i], dc.getEntry(i)); { final DirCacheEntry[] aContents = dc.getEntriesWithin("a"); assertNotNull(aContents); assertEquals(aLast - aFirst + 1, aContents.length); for (int i = aFirst, j = 0; i <= aLast; i++, j++) assertSame(ents[i], aContents[j]); } { final DirCacheEntry[] aContents = dc.getEntriesWithin("a/"); assertNotNull(aContents); assertEquals(aLast - aFirst + 1, aContents.length); for (int i = aFirst, j = 0; i <= aLast; i++, j++) assertSame(ents[i], aContents[j]); } { final DirCacheEntry[] aContents = dc.getEntriesWithin(""); assertNotNull(aContents); assertEquals(ents.length, aContents.length); for (int i = 0; i < ents.length; i++) assertSame(ents[i], aContents[i]); } assertNotNull(dc.getEntriesWithin("a.")); assertEquals(0, dc.getEntriesWithin("a.").length); assertNotNull(dc.getEntriesWithin("a0b")); assertEquals(0, dc.getEntriesWithin("a0b.").length); assertNotNull(dc.getEntriesWithin("zoo")); assertEquals(0, dc.getEntriesWithin("zoo.").length); }
@Test public void testDetectUnmergedPaths() throws Exception { final DirCache dc = db.readDirCache(); final DirCacheEntry[] ents = new DirCacheEntry[3]; ents[0] = new DirCacheEntry("a", 1); ents[0].setFileMode(FileMode.REGULAR_FILE); ents[1] = new DirCacheEntry("a", 2); ents[1].setFileMode(FileMode.REGULAR_FILE); ents[2] = new DirCacheEntry("a", 3); ents[2].setFileMode(FileMode.REGULAR_FILE); final DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.length; i++) b.add(ents[i]); b.finish(); assertTrue(dc.hasUnmergedPaths()); }
/** * Writes a file to the tickets branch. * * @param db * @param file * @param content * @param createdBy * @param msg */ private void writeTicketsFile( Repository db, String file, String content, String createdBy, String msg) { if (getTicketsBranch(db) == null) { createTicketsBranch(db); } DirCache newIndex = DirCache.newInCore(); DirCacheBuilder builder = newIndex.builder(); try (ObjectInserter inserter = db.newObjectInserter()) { // create an index entry for the revised index final DirCacheEntry idIndexEntry = new DirCacheEntry(file); idIndexEntry.setLength(content.length()); idIndexEntry.setLastModified(System.currentTimeMillis()); idIndexEntry.setFileMode(FileMode.REGULAR_FILE); // insert new ticket index idIndexEntry.setObjectId( inserter.insert( org.eclipse.jgit.lib.Constants.OBJ_BLOB, content.getBytes(Constants.ENCODING))); // add to temporary in-core index builder.add(idIndexEntry); Set<String> ignorePaths = new HashSet<String>(); ignorePaths.add(file); for (DirCacheEntry entry : getTreeEntries(db, ignorePaths)) { builder.add(entry); } // finish temporary in-core index used for this commit builder.finish(); // commit the change commitIndex(db, newIndex, createdBy, msg); } catch (ConcurrentRefUpdateException e) { log.error("", e); } catch (IOException e) { log.error("", e); } }
/** * Scan head, index and merge tree. Used during normal checkout or merge operations. * * @throws CorruptObjectException * @throws IOException */ public void preScanTwoTrees() throws CorruptObjectException, IOException { removed.clear(); updated.clear(); conflicts.clear(); walk = new NameConflictTreeWalk(repo); builder = dc.builder(); addTree(walk, headCommitTree); addTree(walk, mergeCommitTree); walk.addTree(new DirCacheBuildIterator(builder)); walk.addTree(workingTree); while (walk.next()) { processEntry( walk.getTree(0, CanonicalTreeParser.class), walk.getTree(1, CanonicalTreeParser.class), walk.getTree(2, DirCacheBuildIterator.class), walk.getTree(3, WorkingTreeIterator.class)); if (walk.isSubtree()) walk.enterSubtree(); } }
@Test public void testBuildThenClear() throws Exception { final DirCache dc = db.readDirCache(); final String[] paths = {"a-", "a.b", "a/b", "a0b"}; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(FileMode.REGULAR_FILE); } final DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.length; i++) b.add(ents[i]); b.finish(); assertFalse(dc.hasUnmergedPaths()); assertEquals(paths.length, dc.getEntryCount()); dc.clear(); assertEquals(0, dc.getEntryCount()); assertFalse(dc.hasUnmergedPaths()); }
/** * Scan index and merge tree (no HEAD). Used e.g. for initial checkout when there is no head yet. * * @throws MissingObjectException * @throws IncorrectObjectTypeException * @throws CorruptObjectException * @throws IOException */ public void prescanOneTree() throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { removed.clear(); updated.clear(); conflicts.clear(); builder = dc.builder(); walk = new NameConflictTreeWalk(repo); walk.addTree(mergeCommitTree); walk.addTree(new DirCacheBuildIterator(builder)); walk.addTree(workingTree); while (walk.next()) { processEntry( walk.getTree(0, CanonicalTreeParser.class), walk.getTree(1, DirCacheBuildIterator.class), walk.getTree(2, WorkingTreeIterator.class)); if (walk.isSubtree()) walk.enterSubtree(); } conflicts.removeAll(removed); }
@Test public void testRejectInvalidWindowsPaths() throws Exception { SystemReader.setInstance( new MockSystemReader() { { setUnix(); } }); String path = "src/con.txt"; DirCache dc = db.lockDirCache(); DirCacheBuilder b = dc.builder(); DirCacheEntry e = new DirCacheEntry(path); e.setFileMode(FileMode.REGULAR_FILE); try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) { e.setObjectId(formatter.idFor(Constants.OBJ_BLOB, Constants.encode(path))); } b.add(e); b.commit(); db.readDirCache(); SystemReader.setInstance( new MockSystemReader() { { setWindows(); } }); try { db.readDirCache(); fail("should have rejected " + path); } catch (CorruptObjectException err) { assertEquals(MessageFormat.format(JGitText.get().invalidPath, path), err.getMessage()); assertNotNull(err.getCause()); assertEquals("invalid name 'CON'", err.getCause().getMessage()); } }
private DirCache createTemporaryIndex(ObjectId headId, DirCache index, RevWalk rw) throws IOException { ObjectInserter inserter = null; // get DirCacheBuilder for existing index DirCacheBuilder existingBuilder = index.builder(); // get DirCacheBuilder for newly created in-core index to build a // temporary index for this commit DirCache inCoreIndex = DirCache.newInCore(); DirCacheBuilder tempBuilder = inCoreIndex.builder(); onlyProcessed = new boolean[only.size()]; boolean emptyCommit = true; try (TreeWalk treeWalk = new TreeWalk(repo)) { treeWalk.setOperationType(OperationType.CHECKIN_OP); int dcIdx = treeWalk.addTree(new DirCacheBuildIterator(existingBuilder)); FileTreeIterator fti = new FileTreeIterator(repo); fti.setDirCacheIterator(treeWalk, 0); int fIdx = treeWalk.addTree(fti); int hIdx = -1; if (headId != null) hIdx = treeWalk.addTree(rw.parseTree(headId)); treeWalk.setRecursive(true); String lastAddedFile = null; while (treeWalk.next()) { String path = treeWalk.getPathString(); // check if current entry's path matches a specified path int pos = lookupOnly(path); CanonicalTreeParser hTree = null; if (hIdx != -1) hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class); DirCacheIterator dcTree = treeWalk.getTree(dcIdx, DirCacheIterator.class); if (pos >= 0) { // include entry in commit FileTreeIterator fTree = treeWalk.getTree(fIdx, FileTreeIterator.class); // check if entry refers to a tracked file boolean tracked = dcTree != null || hTree != null; if (!tracked) continue; // for an unmerged path, DirCacheBuildIterator will yield 3 // entries, we only want to add one if (path.equals(lastAddedFile)) continue; lastAddedFile = path; if (fTree != null) { // create a new DirCacheEntry with data retrieved from // disk final DirCacheEntry dcEntry = new DirCacheEntry(path); long entryLength = fTree.getEntryLength(); dcEntry.setLength(entryLength); dcEntry.setLastModified(fTree.getEntryLastModified()); dcEntry.setFileMode(fTree.getIndexFileMode(dcTree)); boolean objectExists = (dcTree != null && fTree.idEqual(dcTree)) || (hTree != null && fTree.idEqual(hTree)); if (objectExists) { dcEntry.setObjectId(fTree.getEntryObjectId()); } else { if (FileMode.GITLINK.equals(dcEntry.getFileMode())) dcEntry.setObjectId(fTree.getEntryObjectId()); 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(); } } } // add to existing index existingBuilder.add(dcEntry); // add to temporary in-core index tempBuilder.add(dcEntry); if (emptyCommit && (hTree == null || !hTree.idEqual(fTree) || hTree.getEntryRawMode() != fTree.getEntryRawMode())) // this is a change emptyCommit = false; } else { // if no file exists on disk, neither add it to // index nor to temporary in-core index 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 final DirCacheEntry dcEntry = new DirCacheEntry(path); dcEntry.setObjectId(hTree.getEntryObjectId()); dcEntry.setFileMode(hTree.getEntryFileMode()); // add to temporary in-core index tempBuilder.add(dcEntry); } // preserve existing entry in index if (dcTree != null) existingBuilder.add(dcTree.getDirCacheEntry()); } } } // 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.get(i))); // there must be at least one change if (emptyCommit) throw new JGitInternalException(JGitText.get().emptyCommit); // update index existingBuilder.commit(); // finish temporary in-core index used for this commit tempBuilder.finish(); return inCoreIndex; }
/** * Deletes a ticket from the repository. * * @param ticket * @return true if successful */ @Override protected synchronized boolean deleteTicketImpl( RepositoryModel repository, TicketModel ticket, String deletedBy) { if (ticket == null) { throw new RuntimeException("must specify a ticket!"); } boolean success = false; Repository db = repositoryManager.getRepository(ticket.repository); try { RefModel ticketsBranch = getTicketsBranch(db); if (ticketsBranch == null) { throw new RuntimeException(BRANCH + " does not exist!"); } String ticketPath = toTicketPath(ticket.number); try { ObjectId treeId = db.resolve(BRANCH + "^{tree}"); // Create the in-memory index of the new/updated ticket DirCache index = DirCache.newInCore(); DirCacheBuilder builder = index.builder(); // Traverse HEAD to add all other paths try (TreeWalk treeWalk = new TreeWalk(db)) { int hIdx = -1; if (treeId != null) { hIdx = treeWalk.addTree(treeId); } treeWalk.setRecursive(true); while (treeWalk.next()) { String path = treeWalk.getPathString(); CanonicalTreeParser hTree = null; if (hIdx != -1) { hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class); } if (!path.startsWith(ticketPath)) { // add entries from HEAD for all other paths if (hTree != null) { final DirCacheEntry entry = new DirCacheEntry(path); entry.setObjectId(hTree.getEntryObjectId()); entry.setFileMode(hTree.getEntryFileMode()); // add to temporary in-core index builder.add(entry); } } } } // finish temporary in-core index used for this commit builder.finish(); success = commitIndex(db, index, deletedBy, "- " + ticket.number); } catch (Throwable t) { log.error( MessageFormat.format( "Failed to delete ticket {0,number,0} from {1}", ticket.number, db.getDirectory()), t); } } finally { db.close(); } return success; }