private boolean processOne(Candidate n) throws IOException {
    RevCommit parent = n.getParent(0);
    if (parent == null) return split(n.getNextCandidate(0), n);
    revPool.parseHeaders(parent);

    if (find(parent, n.sourcePath)) {
      if (idBuf.equals(n.sourceBlob)) return blameEntireRegionOnParent(n, parent);
      return splitBlameWithParent(n, parent);
    }

    if (n.sourceCommit == null) return result(n);

    DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath);
    if (r == null) return result(n);

    if (0 == r.getOldId().prefixCompare(n.sourceBlob)) {
      // A 100% rename without any content change can also
      // skip directly to the parent.
      n.sourceCommit = parent;
      n.sourcePath = PathFilter.create(r.getOldPath());
      push(n);
      return false;
    }

    Candidate next = n.create(parent, PathFilter.create(r.getOldPath()));
    next.sourceBlob = r.getOldId().toObjectId();
    next.renameScore = r.getScore();
    next.loadText(reader);
    return split(next, n);
  }
  private boolean processMerge(Candidate n) throws IOException {
    int pCnt = n.getParentCount();

    // If any single parent exactly matches the merge, follow only
    // that one parent through history.
    ObjectId[] ids = null;
    for (int pIdx = 0; pIdx < pCnt; pIdx++) {
      RevCommit parent = n.getParent(pIdx);
      revPool.parseHeaders(parent);
      if (!find(parent, n.sourcePath)) continue;
      if (!(n instanceof ReverseCandidate) && idBuf.equals(n.sourceBlob))
        return blameEntireRegionOnParent(n, parent);
      if (ids == null) ids = new ObjectId[pCnt];
      ids[pIdx] = idBuf.toObjectId();
    }

    // If rename detection is enabled, search for any relevant names.
    DiffEntry[] renames = null;
    if (renameDetector != null) {
      renames = new DiffEntry[pCnt];
      for (int pIdx = 0; pIdx < pCnt; pIdx++) {
        RevCommit parent = n.getParent(pIdx);
        if (ids != null && ids[pIdx] != null) continue;

        DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath);
        if (r == null) continue;

        if (n instanceof ReverseCandidate) {
          if (ids == null) ids = new ObjectId[pCnt];
          ids[pCnt] = r.getOldId().toObjectId();
        } else if (0 == r.getOldId().prefixCompare(n.sourceBlob)) {
          // A 100% rename without any content change can also
          // skip directly to the parent. Note this bypasses an
          // earlier parent that had the path (above) but did not
          // have an exact content match. For performance reasons
          // we choose to follow the one parent over trying to do
          // possibly both parents.
          n.sourcePath = PathFilter.create(r.getOldPath());
          return blameEntireRegionOnParent(n, parent);
        }

        renames[pIdx] = r;
      }
    }

    // Construct the candidate for each parent.
    Candidate[] parents = new Candidate[pCnt];
    for (int pIdx = 0; pIdx < pCnt; pIdx++) {
      RevCommit parent = n.getParent(pIdx);

      Candidate p;
      if (renames != null && renames[pIdx] != null) {
        p = n.create(parent, PathFilter.create(renames[pIdx].getOldPath()));
        p.renameScore = renames[pIdx].getScore();
        p.sourceBlob = renames[pIdx].getOldId().toObjectId();
      } else if (ids != null && ids[pIdx] != null) {
        p = n.create(parent, n.sourcePath);
        p.sourceBlob = ids[pIdx];
      } else {
        continue;
      }

      EditList editList;
      if (n instanceof ReverseCandidate && p.sourceBlob.equals(n.sourceBlob)) {
        // This special case happens on ReverseCandidate forks.
        p.sourceText = n.sourceText;
        editList = new EditList(0);
      } else {
        p.loadText(reader);
        editList = diffAlgorithm.diff(textComparator, p.sourceText, n.sourceText);
      }

      if (editList.isEmpty()) {
        // Ignoring whitespace (or some other special comparator) can
        // cause non-identical blobs to have an empty edit list. In
        // a case like this push the parent alone.
        if (n instanceof ReverseCandidate) {
          parents[pIdx] = p;
          continue;
        }

        p.regionList = n.regionList;
        n.regionList = null;
        parents[pIdx] = p;
        break;
      }

      p.takeBlame(editList, n);

      // Only remember this parent candidate if there is at least
      // one region that was blamed on the parent.
      if (p.regionList != null) {
        // Reverse blame requires inverting the regions. This puts
        // the regions the parent deleted from us into the parent,
        // and retains the common regions to look at other parents
        // for deletions.
        if (n instanceof ReverseCandidate) {
          Region r = p.regionList;
          p.regionList = n.regionList;
          n.regionList = r;
        }

        parents[pIdx] = p;
      }
    }

    if (n instanceof ReverseCandidate) {
      // On a reverse blame report all deletions found in the children,
      // and pass on to them a copy of our region list.
      Candidate resultHead = null;
      Candidate resultTail = null;

      for (int pIdx = 0; pIdx < pCnt; pIdx++) {
        Candidate p = parents[pIdx];
        if (p == null) continue;

        if (p.regionList != null) {
          Candidate r = p.copy(p.sourceCommit);
          if (resultTail != null) {
            resultTail.queueNext = r;
            resultTail = r;
          } else {
            resultHead = r;
            resultTail = r;
          }
        }

        if (n.regionList != null) {
          p.regionList = n.regionList.deepCopy();
          push(p);
        }
      }

      if (resultHead != null) return result(resultHead);
      return false;
    }

    // Push any parents that are still candidates.
    for (int pIdx = 0; pIdx < pCnt; pIdx++) {
      if (parents[pIdx] != null) push(parents[pIdx]);
    }

    if (n.regionList != null) return result(n);
    return false;
  }
Exemple #3
0
  public static CherryPickResult cherryPickNoMerge(final Git git, Ref src)
      throws GitAPIException, CantMergeCommitWithZeroParentsException {
    // Does the same as the original git-cherryPick
    // except commiting after running merger
    Repository repo = git.getRepository();

    RevCommit newHead = null;
    List<Ref> cherryPickedRefs = new LinkedList<Ref>();

    RevWalk revWalk = new RevWalk(repo);
    try {
      // get the head commit
      Ref headRef = repo.getRef(Constants.HEAD);
      if (headRef == null)
        throw new NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);
      RevCommit headCommit = revWalk.parseCommit(headRef.getObjectId());

      newHead = headCommit;

      // get the commit to be cherry-picked
      // handle annotated tags
      ObjectId srcObjectId = src.getPeeledObjectId();
      if (srcObjectId == null) srcObjectId = src.getObjectId();
      RevCommit srcCommit = revWalk.parseCommit(srcObjectId);

      // get the parent of the commit to cherry-pick
      if (srcCommit.getParentCount() == 0)
        throw new CantMergeCommitWithZeroParentsException(
            "Commit with zero parents cannot be merged");

      if (srcCommit.getParentCount() > 1)
        throw new MultipleParentsNotAllowedException(
            MessageFormat.format(
                JGitText.get().canOnlyCherryPickCommitsWithOneParent,
                srcCommit.name(),
                Integer.valueOf(srcCommit.getParentCount())));

      RevCommit srcParent = srcCommit.getParent(0);
      revWalk.parseHeaders(srcParent);

      ResolveMerger merger = (ResolveMerger) MergeStrategy.RESOLVE.newMerger(repo);
      merger.setWorkingTreeIterator(new FileTreeIterator(repo));
      merger.setBase(srcParent.getTree());
      if (merger.merge(headCommit, srcCommit)) {
        DirCacheCheckout dco =
            new DirCacheCheckout(
                repo, headCommit.getTree(), repo.lockDirCache(), merger.getResultTreeId());
        dco.setFailOnConflict(true);
        dco.checkout();

        cherryPickedRefs.add(src);
      } else {
        if (merger.failed()) return new CherryPickResult(merger.getFailingPaths());

        // there are merge conflicts
        String message =
            new MergeMessageFormatter()
                .formatWithConflicts(srcCommit.getFullMessage(), merger.getUnmergedPaths());

        repo.writeCherryPickHead(srcCommit.getId());
        repo.writeMergeCommitMsg(message);

        return CherryPickResult.CONFLICT;
      }
    } catch (IOException e) {
      throw new JGitInternalException(
          MessageFormat.format(JGitText.get().exceptionCaughtDuringExecutionOfCherryPickCommand, e),
          e);
    } finally {
      revWalk.release();
    }

    return new CherryPickResult(newHead, cherryPickedRefs);
  }