예제 #1
0
  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);
  }
예제 #2
0
  @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;
  }
예제 #4
0
    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());
    }
예제 #5
0
 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;
 }
예제 #6
0
 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;
 }
예제 #7
0
  /**
   * 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;
    }
  }
예제 #8
0
 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;
   }
 }
예제 #9
0
 /**
  * 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);
 }
예제 #10
0
  private void verifyStageState(StageState expected, int... stages) throws IOException {
    DirCacheBuilder builder = db.lockDirCache().builder();
    for (int stage : stages) {
      DirCacheEntry entry = createEntry("a", FileMode.REGULAR_FILE, stage, "content");
      builder.add(entry);
    }
    builder.commit();

    IndexDiff diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
    diff.diff();

    assertEquals(
        "Conflict for entries in stages " + Arrays.toString(stages),
        expected,
        diff.getConflictingStageStates().get("a"));
  }
예제 #11
0
  @Test
  public void testStageState_simulated_bug() throws Exception {
    try (Git git = new Git(db)) {
      writeTrashFile("a", "content");
      git.add().addFilepattern("a").call();
      RevCommit initialCommit = git.commit().setMessage("initial commit").call();

      // create branch and add a new file
      final String branchName = Constants.R_HEADS + "branch";
      createBranch(initialCommit, branchName);
      checkoutBranch(branchName);
      writeTrashFile("b", "second file content - branch");
      git.add().addFilepattern("b").call();
      git.commit().setMessage("branch commit").call();

      // checkout master and add the same new file
      checkoutBranch(Constants.R_HEADS + Constants.MASTER);
      writeTrashFile("b", "second file content - master");
      git.add().addFilepattern("b").call();
      git.commit().setMessage("master commit").call();

      // Simulate a failed merge of branch into master
      DirCacheBuilder builder = db.lockDirCache().builder();
      DirCacheEntry entry = createEntry("a", FileMode.REGULAR_FILE, 0, "content");
      builder.add(entry);
      entry = createEntry("b", FileMode.REGULAR_FILE, 2, "second file content - master");
      builder.add(entry);
      entry = createEntry("b", FileMode.REGULAR_FILE, 3, "second file content - branch");
      builder.add(entry);
      builder.commit();

      FileTreeIterator iterator = new FileTreeIterator(db);
      IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
      diff.diff();

      assertTrue(diff.getChanged().isEmpty());
      assertTrue(diff.getAdded().isEmpty());
      assertTrue(diff.getRemoved().isEmpty());
      assertTrue(diff.getMissing().isEmpty());
      assertTrue(diff.getModified().isEmpty());
      assertEquals(1, diff.getConflicting().size());
      assertTrue(diff.getConflicting().contains("b"));
      assertEquals(StageState.BOTH_ADDED, diff.getConflictingStageStates().get("b"));
      assertTrue(diff.getUntrackedFolders().isEmpty());
    }
  }
예제 #12
0
 /**
  * adds a entry to the index builder which is a copy of the specified DirCacheEntry
  *
  * @param e the entry which should be copied
  * @return the entry which was added to the index
  */
 private DirCacheEntry keep(DirCacheEntry e) {
   DirCacheEntry newEntry = new DirCacheEntry(e.getPathString(), e.getStage());
   newEntry.setFileMode(e.getFileMode());
   newEntry.setObjectId(e.getObjectId());
   newEntry.setLastModified(e.getLastModified());
   newEntry.setLength(e.getLength());
   builder.add(newEntry);
   return newEntry;
 }
  /**
   * 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);
    }
  }
예제 #14
0
 /**
  * adds a new path with the specified stage to the index builder
  *
  * @param path
  * @param p
  * @param stage
  * @param lastMod
  * @param len
  * @return the entry which was added to the index
  */
 private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage, long lastMod, long len) {
   if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
     DirCacheEntry e = new DirCacheEntry(path, stage);
     e.setFileMode(p.getEntryFileMode());
     e.setObjectId(p.getEntryObjectId());
     e.setLastModified(lastMod);
     e.setLength(len);
     builder.add(e);
     return e;
   }
   return null;
 }
예제 #15
0
  /**
   * 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.
   *
   * @param base
   * @param ours
   * @param theirs
   * @param result
   * @throws FileNotFoundException
   * @throws IOException
   */
  private void updateIndex(
      CanonicalTreeParser base,
      CanonicalTreeParser ours,
      CanonicalTreeParser theirs,
      MergeResult<RawText> result)
      throws FileNotFoundException, IOException {
    File mergedFile = !inCore ? writeMergedFile(result) : null;
    if (result.containsConflicts()) {
      // A conflict occurred, the file will contain conflict markers
      // the index will be populated with the three stages and the
      // workdir (if used) contains the halfway merged content.
      add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
      add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
      add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
      mergeResults.put(tw.getPathString(), result);
      return;
    }

    // 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.getPathString());

    // Set the mode for the new content. Fall back to REGULAR_FILE if
    // we can't merge modes of OURS and THEIRS.
    int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1), tw.getRawMode(2));
    dce.setFileMode(
        newMode == FileMode.MISSING.getBits() ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
    if (mergedFile != null) {
      long len = mergedFile.length();
      dce.setLastModified(mergedFile.lastModified());
      dce.setLength((int) len);
      InputStream is = new FileInputStream(mergedFile);
      try {
        dce.setObjectId(getObjectInserter().insert(OBJ_BLOB, len, is));
      } finally {
        is.close();
      }
    } else dce.setObjectId(insertMergeResult(result));
    builder.add(dce);
  }
예제 #16
0
  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;
  }