@Override
  protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
    MarkdownConfig cfg = MarkdownConfig.get(getAccess(req).getConfig());
    if (!cfg.render) {
      res.setStatus(SC_NOT_FOUND);
      return;
    }

    GitilesView view = ViewFilter.getView(req);
    Repository repo = ServletUtils.getRepository(req);
    try (RevWalk rw = new RevWalk(repo)) {
      ObjectReader reader = rw.getObjectReader();
      String path = view.getPathPart();
      RevTree root;
      try {
        root = rw.parseTree(view.getRevision().getId());
      } catch (IncorrectObjectTypeException e) {
        res.setStatus(SC_NOT_FOUND);
        return;
      }

      MarkdownFile srcmd = findFile(rw, root, path);
      if (srcmd == null) {
        res.setStatus(SC_NOT_FOUND);
        return;
      }

      MarkdownFile navmd = findFile(rw, root, NAVBAR_MD);
      String curEtag = etag(srcmd, navmd);
      if (etagMatch(req, curEtag)) {
        res.setStatus(SC_NOT_MODIFIED);
        return;
      }

      view = view.toBuilder().setPathPart(srcmd.path).build();
      try {
        srcmd.read(reader, cfg);
        if (navmd != null) {
          navmd.read(reader, cfg);
        }
      } catch (LargeObjectException.ExceedsLimit errBig) {
        fileTooBig(res, view, errBig);
        return;
      } catch (IOException err) {
        readError(res, view, err);
        return;
      }

      MarkdownToHtml.Builder fmt =
          MarkdownToHtml.builder()
              .setConfig(cfg)
              .setGitilesView(view)
              .setRequestUri(req.getRequestURI())
              .setReader(reader)
              .setRootTree(root);
      res.setHeader(HttpHeaders.ETAG, curEtag);
      showDoc(req, res, view, cfg, fmt, navmd, srcmd);
    }
  }
Beispiel #2
0
  private static AnyObjectId asTree(ObjectReader or, AnyObjectId treeish)
      throws MissingObjectException, IncorrectObjectTypeException, IOException {
    if (treeish instanceof RevTree) return treeish;

    if (treeish instanceof RevCommit && ((RevCommit) treeish).getTree() != null)
      return ((RevCommit) treeish).getTree();

    try (RevWalk rw = new RevWalk(or)) {
      return rw.parseTree(treeish).getId();
    }
  }
Beispiel #3
0
 /**
  * Checks if a file with the given path exists in the HEAD tree
  *
  * @param path
  * @return true if the file exists
  * @throws IOException
  */
 private boolean inHead(String path) throws IOException {
   ObjectId headId = db.resolve(Constants.HEAD);
   RevWalk rw = new RevWalk(db);
   TreeWalk tw = null;
   try {
     tw = TreeWalk.forPath(db, path, rw.parseTree(headId));
     return tw != null;
   } finally {
     rw.release();
     rw.dispose();
     if (tw != null) tw.release();
   }
 }
Beispiel #4
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;
   }
 }
Beispiel #5
0
  /**
   * Merge together two or more tree-ish objects.
   *
   * <p>Any tree-ish may be supplied as inputs. Commits and/or tags pointing at trees or commits may
   * be passed as input objects.
   *
   * @param tips source trees to be combined together. The merge base is not included in this set.
   * @return true if the merge was completed without conflicts; false if the merge strategy cannot
   *     handle this merge or there were conflicts preventing it from automatically resolving all
   *     paths.
   * @throws IncorrectObjectTypeException one of the input objects is not a commit, but the strategy
   *     requires it to be a commit.
   * @throws IOException one or more sources could not be read, or outputs could not be written to
   *     the Repository.
   */
  public boolean merge(final AnyObjectId[] tips) throws IOException {
    sourceObjects = new RevObject[tips.length];
    for (int i = 0; i < tips.length; i++) sourceObjects[i] = walk.parseAny(tips[i]);

    sourceCommits = new RevCommit[sourceObjects.length];
    for (int i = 0; i < sourceObjects.length; i++) {
      try {
        sourceCommits[i] = walk.parseCommit(sourceObjects[i]);
      } catch (IncorrectObjectTypeException err) {
        sourceCommits[i] = null;
      }
    }

    sourceTrees = new RevTree[sourceObjects.length];
    for (int i = 0; i < sourceObjects.length; i++)
      sourceTrees[i] = walk.parseTree(sourceObjects[i]);

    return mergeImpl();
  }
Beispiel #6
0
  /**
   * Determine the differences between two trees.
   *
   * <p>No output is created, instead only the file paths that are different are returned. Callers
   * may choose to format these paths themselves, or convert them into {@link FileHeader} instances
   * with a complete edit list by calling {@link #toFileHeader(DiffEntry)}.
   *
   * @param a the old (or previous) side.
   * @param b the new (or updated) side.
   * @return the paths that are different.
   * @throws IOException trees cannot be read or file contents cannot be read.
   */
  public List<DiffEntry> scan(AnyObjectId a, AnyObjectId b) throws IOException {
    assertHaveRepository();

    RevWalk rw = new RevWalk(reader);
    return scan(rw.parseTree(a), rw.parseTree(b));
  }
Beispiel #7
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;
  }
Beispiel #8
0
  /**
   * Open a batch of updates to the same metadata ref.
   *
   * <p>This allows making multiple commits to a single metadata ref, at the end of which is a
   * single ref update. For batching together updates to multiple refs (each consisting of one or
   * more commits against their respective refs), create the {@link MetaDataUpdate} with a {@link
   * BatchRefUpdate}.
   *
   * <p>A ref update produced by this {@link BatchMetaDataUpdate} is only committed if there is no
   * associated {@link BatchRefUpdate}. As a result, the configured ref updated event is not fired
   * if there is an associated batch.
   *
   * @param update helper info about the update.
   * @throws IOException if the update failed.
   */
  public BatchMetaDataUpdate openUpdate(final MetaDataUpdate update) throws IOException {
    final Repository db = update.getRepository();

    reader = db.newObjectReader();
    inserter = db.newObjectInserter();
    final RevWalk rw = new RevWalk(reader);
    final RevTree tree = revision != null ? rw.parseTree(revision) : null;
    newTree = readTree(tree);
    return new BatchMetaDataUpdate() {
      AnyObjectId src = revision;
      AnyObjectId srcTree = tree;

      @Override
      public void write(CommitBuilder commit) throws IOException {
        write(VersionedMetaData.this, commit);
      }

      private boolean doSave(VersionedMetaData config, CommitBuilder commit) throws IOException {
        DirCache nt = config.newTree;
        ObjectReader r = config.reader;
        ObjectInserter i = config.inserter;
        try {
          config.newTree = newTree;
          config.reader = reader;
          config.inserter = inserter;
          return config.onSave(commit);
        } catch (ConfigInvalidException e) {
          throw new IOException(
              "Cannot update " + getRefName() + " in " + db.getDirectory() + ": " + e.getMessage(),
              e);
        } finally {
          config.newTree = nt;
          config.reader = r;
          config.inserter = i;
        }
      }

      @Override
      public void write(VersionedMetaData config, CommitBuilder commit) throws IOException {
        if (!doSave(config, commit)) {
          return;
        }

        // Reuse tree from parent commit unless there are contents in newTree or
        // there is no tree for a parent commit.
        ObjectId res =
            newTree.getEntryCount() != 0 || srcTree == null
                ? newTree.writeTree(inserter)
                : srcTree.copy();
        if (res.equals(srcTree) && !update.allowEmpty() && (commit.getTreeId() == null)) {
          // If there are no changes to the content, don't create the commit.
          return;
        }

        // If changes are made to the DirCache and those changes are written as
        // a commit and then the tree ID is set for the CommitBuilder, then
        // those previous DirCache changes will be ignored and the commit's
        // tree will be replaced with the ID in the CommitBuilder. The same is
        // true if you explicitly set tree ID in a commit and then make changes
        // to the DirCache; that tree ID will be ignored and replaced by that of
        // the tree for the updated DirCache.
        if (commit.getTreeId() == null) {
          commit.setTreeId(res);
        } else {
          // In this case, the caller populated the tree without using DirCache.
          res = commit.getTreeId();
        }

        if (src != null) {
          commit.addParentId(src);
        }

        if (update.insertChangeId()) {
          ObjectId id =
              ChangeIdUtil.computeChangeId(
                  res,
                  getRevision(),
                  commit.getAuthor(),
                  commit.getCommitter(),
                  commit.getMessage());
          commit.setMessage(ChangeIdUtil.insertId(commit.getMessage(), id));
        }

        src = inserter.insert(commit);
        srcTree = res;
      }

      @Override
      public RevCommit createRef(String refName) throws IOException {
        if (Objects.equals(src, revision)) {
          return revision;
        }
        return updateRef(ObjectId.zeroId(), src, refName);
      }

      @Override
      public void removeRef(String refName) throws IOException {
        RefUpdate ru = db.updateRef(refName);
        ru.setForceUpdate(true);
        if (revision != null) {
          ru.setExpectedOldObjectId(revision);
        }
        RefUpdate.Result result = ru.delete();
        switch (result) {
          case FORCED:
            update.fireGitRefUpdatedEvent(ru);
            return;
          case FAST_FORWARD:
          case IO_FAILURE:
          case LOCK_FAILURE:
          case NEW:
          case NOT_ATTEMPTED:
          case NO_CHANGE:
          case REJECTED:
          case REJECTED_CURRENT_BRANCH:
          case RENAMED:
          default:
            throw new IOException(
                "Cannot delete "
                    + ru.getName()
                    + " in "
                    + db.getDirectory()
                    + ": "
                    + ru.getResult());
        }
      }

      @Override
      public RevCommit commit() throws IOException {
        return commitAt(revision);
      }

      @Override
      public RevCommit commitAt(ObjectId expected) throws IOException {
        if (Objects.equals(src, expected)) {
          return revision;
        }
        return updateRef(MoreObjects.firstNonNull(expected, ObjectId.zeroId()), src, getRefName());
      }

      @Override
      public void close() {
        newTree = null;

        rw.close();
        if (inserter != null) {
          inserter.close();
          inserter = null;
        }

        if (reader != null) {
          reader.close();
          reader = null;
        }
      }

      private RevCommit updateRef(AnyObjectId oldId, AnyObjectId newId, String refName)
          throws IOException {
        BatchRefUpdate bru = update.getBatch();
        if (bru != null) {
          bru.addCommand(new ReceiveCommand(oldId.toObjectId(), newId.toObjectId(), refName));
          inserter.flush();
          revision = rw.parseCommit(newId);
          return revision;
        }

        RefUpdate ru = db.updateRef(refName);
        ru.setExpectedOldObjectId(oldId);
        ru.setNewObjectId(src);
        ru.setRefLogIdent(update.getCommitBuilder().getAuthor());
        String message = update.getCommitBuilder().getMessage();
        if (message == null) {
          message = "meta data update";
        }
        try (BufferedReader reader = new BufferedReader(new StringReader(message))) {
          // read the subject line and use it as reflog message
          ru.setRefLogMessage("commit: " + reader.readLine(), true);
        }
        inserter.flush();
        RefUpdate.Result result = ru.update();
        switch (result) {
          case NEW:
          case FAST_FORWARD:
            revision = rw.parseCommit(ru.getNewObjectId());
            update.fireGitRefUpdatedEvent(ru);
            return revision;
          case FORCED:
          case IO_FAILURE:
          case LOCK_FAILURE:
          case NOT_ATTEMPTED:
          case NO_CHANGE:
          case REJECTED:
          case REJECTED_CURRENT_BRANCH:
          case RENAMED:
          default:
            throw new IOException(
                "Cannot update "
                    + ru.getName()
                    + " in "
                    + db.getDirectory()
                    + ": "
                    + ru.getResult());
        }
      }
    };
  }