Ejemplo n.º 1
0
  public void buildCompositeCommits() throws IOException {
    revWalk = new RevWalk(repository);
    ByteArrayOutputStream diffTexts = new ByteArrayOutputStream();
    DiffFormatter df = new DiffFormatter(diffTexts);
    df.setRepository(repository);
    df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
    df.setContext(0);
    df.setDiffAlgorithm(DiffAlgorithm.getAlgorithm(SupportedAlgorithm.HISTOGRAM));
    df.setDetectRenames(true);
    for (int idx = 0; idx < _commits.size(); idx++) {
      RevCommit commit = revWalk.parseCommit(_commits.get(idx));
      int p_count = commit.getParentCount();
      if (p_count == 0) {
        throw new RuntimeException("commit with no parent ?!?!");
      }
      RevCommit p = revWalk.parseCommit(commit.getParent(0).getId());
      List<DiffEntry> diffs = df.scan(p.getTree(), commit.getTree());
      for (DiffEntry d : diffs) {
        CompositeDiff cd = new CompositeDiff(d, commit);

        if (ParsingUtils.isSourceFile(d.getOldPath())
            || ParsingUtils.isSourceFile(d.getNewPath())) {
          extractCodeEdits(diffTexts, df, d, cd);
        }
        _diffs.add(cd);
      }
    }
    revWalk.release();
  }
 @Override
 public boolean include(final RevCommit commit, final Collection<DiffEntry> diffs)
     throws IOException {
   for (DiffEntry diff : diffs) {
     final AbbreviatedObjectId oldId = diff.getOldId();
     if (oldId == null) continue;
     if (EMPTY_BLOB_ID.equals(oldId.toObjectId())
         && !EMPTY_BLOB_ID.equals(diff.getNewId().toObjectId())) return true;
   }
   return false;
 }
Ejemplo n.º 3
0
  private DiffEntry findRename(RevCommit parent, RevCommit commit, PathFilter path)
      throws IOException {
    if (renameDetector == null) return null;

    treeWalk.setFilter(TreeFilter.ANY_DIFF);
    treeWalk.reset(parent.getTree(), commit.getTree());
    renameDetector.reset();
    renameDetector.addAll(DiffEntry.scan(treeWalk));
    for (DiffEntry ent : renameDetector.compute()) {
      if (isRename(ent) && ent.getNewPath().equals(path.getPath())) return ent;
    }
    return null;
  }
Ejemplo n.º 4
0
  /**
   * Returns the diff between the two commits for the specified file or folder formatted as a patch.
   *
   * @param repository
   * @param baseCommit if base commit is unspecified, the patch is generated against the primary
   *     parent of the specified commit.
   * @param commit
   * @param path if path is specified, the patch is generated only for the specified file or folder.
   *     if unspecified, the patch is generated for the entire diff between the two commits.
   * @return patch as a string
   */
  public static String getCommitPatch(
      Repository repository, RevCommit baseCommit, RevCommit commit, String path) {
    String diff = null;
    try {
      final ByteArrayOutputStream os = new ByteArrayOutputStream();
      RawTextComparator cmp = RawTextComparator.DEFAULT;
      PatchFormatter df = new PatchFormatter(os);
      df.setRepository(repository);
      df.setDiffComparator(cmp);
      df.setDetectRenames(true);

      RevTree commitTree = commit.getTree();
      RevTree baseTree;
      if (baseCommit == null) {
        if (commit.getParentCount() > 0) {
          final RevWalk rw = new RevWalk(repository);
          RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
          baseTree = parent.getTree();
        } else {
          // FIXME initial commit. no parent?!
          baseTree = commitTree;
        }
      } else {
        baseTree = baseCommit.getTree();
      }

      List<DiffEntry> diffEntries = df.scan(baseTree, commitTree);
      if (path != null && path.length() > 0) {
        for (DiffEntry diffEntry : diffEntries) {
          if (diffEntry.getNewPath().equalsIgnoreCase(path)) {
            df.format(diffEntry);
            break;
          }
        }
      } else {
        df.format(diffEntries);
      }
      diff = df.getPatch(commit);
      df.flush();
    } catch (Throwable t) {
      try {
        error(t, repository, "failed to generate commit diff!");
      } catch (Exception e) {
      }
    }
    return diff;
  }
 @Override
 public void format(DiffEntry ent) throws IOException {
   currentPath = diffStat.addPath(ent);
   nofLinesCurrent = 0;
   isOff = false;
   entry = ent;
   if (!truncated) {
     totalNofLinesPrevious = totalNofLinesCurrent;
     if (globalDiffLimit > 0 && totalNofLinesPrevious > globalDiffLimit) {
       truncated = true;
       isOff = true;
     }
     truncateTo = os.size();
   } else {
     isOff = true;
   }
   if (truncated) {
     skipped.add(ent);
   } else {
     // Produce a header here and now
     String path;
     String id;
     if (ChangeType.DELETE.equals(ent.getChangeType())) {
       path = ent.getOldPath();
       id = ent.getOldId().name();
     } else {
       path = ent.getNewPath();
       id = ent.getNewId().name();
     }
     StringBuilder sb =
         new StringBuilder(
             MessageFormat.format(
                 "<div class='header'><div class=\"diffHeader\" id=\"n{0}\"><i class=\"icon-file\"></i> ",
                 id));
     sb.append(StringUtils.escapeForHtml(path, false)).append("</div></div>");
     sb.append("<div class=\"diff\"><table cellpadding='0'><tbody>\n");
     os.write(sb.toString().getBytes());
   }
   // Keep formatting, but if off, don't produce anything anymore. We just keep on counting.
   super.format(ent);
   if (!truncated) {
     // Close the table
     os.write("</tbody></table></div>\n".getBytes());
   }
 }
Ejemplo n.º 6
0
 @Override
 public boolean include(final RevCommit commit, final Collection<DiffEntry> diffs) {
   int count = 0;
   for (DiffEntry diff : diffs)
     for (Edit edit :
         BlobUtils.diff(repository, diff.getOldId().toObjectId(), diff.getNewId().toObjectId()))
       switch (edit.getType()) {
         case DELETE:
           count += edit.getLengthA();
           break;
         case INSERT:
         case REPLACE:
           count += edit.getLengthB();
           break;
         default:
           break;
       }
   return include(commit, diffs, count) ? true : include(false);
 }
Ejemplo n.º 7
0
  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);
  }
 /**
  * Writes an initial table row containing information about added/removed/renamed/copied files. In
  * case of a deletion, we also suppress generating the diff; it's not interesting. (All lines
  * removed.)
  */
 private void handleChange() {
   // XXX Would be nice if we could generate blob links for the cases handled here. Alas, we lack
   // the repo
   // name, and cannot reliably determine it here. We could get the .git directory of a Repository,
   // if we
   // passed in the repo, and then take the name of the parent directory, but that'd fail for repos
   // nested
   // in GitBlit projects. And we don't know if the repo is inside a project or is a top-level
   // repo.
   //
   // That's certainly solvable (just pass along more information), but would require a larger
   // rewrite than
   // I'm prepared to do now.
   String message;
   switch (entry.getChangeType()) {
     case ADD:
       message = getMsg("gb.diffNewFile", "New file");
       break;
     case DELETE:
       message = getMsg("gb.diffDeletedFile", "File was deleted");
       isOff = true;
       break;
     case RENAME:
       message =
           MessageFormat.format(
               getMsg("gb.diffRenamedFile", "File was renamed from {0}"), entry.getOldPath());
       break;
     case COPY:
       message =
           MessageFormat.format(
               getMsg("gb.diffCopiedFile", "File was copied from {0}"), entry.getOldPath());
       break;
     default:
       return;
   }
   writeFullWidthLine(message);
 }
Ejemplo n.º 9
0
 private Path path(final DiffEntry s) {
   final ChangeType changeType = s.getChangeType();
   switch (changeType) {
     case DELETE:
       return Paths.get(s.getOldPath());
     case ADD:
       return Paths.get(s.getNewPath());
     case COPY:
       return Paths.get(s.getNewPath());
     case MODIFY:
       return Paths.get(s.getNewPath());
     case RENAME:
       return Paths.get(s.getNewPath());
     default:
       throw new RuntimeException("Unrecognized change type: " + changeType);
   }
 }
 /**
  * Workaround function for complex private methods in DiffFormatter. This sets the html for the
  * diff headers.
  *
  * @return
  */
 public String getHtml() {
   String html = RawParseUtils.decode(os.toByteArray());
   String[] lines = html.split("\n");
   StringBuilder sb = new StringBuilder();
   for (String line : lines) {
     if (line.startsWith("index")) {
       // skip index lines
     } else if (line.startsWith("new file") || line.startsWith("deleted file")) {
       // skip new file lines
     } else if (line.startsWith("\\ No newline")) {
       // skip no new line
     } else if (line.startsWith("---") || line.startsWith("+++")) {
       // skip --- +++ lines
     } else if (line.startsWith("diff")) {
       // skip diff lines
     } else {
       boolean gitLinkDiff =
           line.length() > 0 && line.substring(1).startsWith("Subproject commit");
       if (gitLinkDiff) {
         sb.append("<tr><th class='diff-line'></th><th class='diff-line'></th>");
         if (line.charAt(0) == '+') {
           sb.append("<th class='diff-state diff-state-add'></th><td class=\"diff-cell add2\">");
         } else {
           sb.append(
               "<th class='diff-state diff-state-sub'></th><td class=\"diff-cell remove2\">");
         }
         line = StringUtils.escapeForHtml(line.substring(1), false);
       }
       sb.append(line);
       if (gitLinkDiff) {
         sb.append("</td></tr>");
       }
       sb.append('\n');
     }
   }
   if (truncated) {
     sb.append(
         MessageFormat.format(
             "<div class='header'><div class='diffHeader'>{0}</div></div>",
             StringUtils.escapeForHtml(
                 getMsg("gb.diffTruncated", "Diff truncated after the above file"), false)));
     // List all files not shown. We can be sure we do have at least one path in skipped.
     sb.append(
         "<div class='diff'><table cellpadding='0'><tbody><tr><td class='diff-cell' colspan='4'>");
     String deletedSuffix =
         StringUtils.escapeForHtml(getMsg("gb.diffDeletedFileSkipped", "(deleted)"), false);
     boolean first = true;
     for (DiffEntry entry : skipped) {
       if (!first) {
         sb.append('\n');
       }
       if (ChangeType.DELETE.equals(entry.getChangeType())) {
         sb.append(
             "<span id=\"n"
                 + entry.getOldId().name()
                 + "\">"
                 + StringUtils.escapeForHtml(entry.getOldPath(), false)
                 + ' '
                 + deletedSuffix
                 + "</span>");
       } else {
         sb.append(
             "<span id=\"n"
                 + entry.getNewId().name()
                 + "\">"
                 + StringUtils.escapeForHtml(entry.getNewPath(), false)
                 + "</span>");
       }
       first = false;
     }
     skipped.clear();
     sb.append("</td></tr></tbody></table></div>");
   }
   return sb.toString();
 }
Ejemplo n.º 11
0
  public static List<PathChangeModel> getFilesInCommit(Repository repository, RevCommit commit) {
    List<PathChangeModel> list = new ArrayList<PathChangeModel>();
    if (!hasCommits(repository)) {
      return list;
    }
    RevWalk rw = new RevWalk(repository);
    try {
      if (commit == null) {
        ObjectId object = getDefaultBranch(repository);
        commit = rw.parseCommit(object);
      }

      if (commit.getParentCount() == 0) {
        TreeWalk tw = new TreeWalk(repository);
        tw.reset();
        tw.setRecursive(true);
        tw.addTree(commit.getTree());
        while (tw.next()) {
          list.add(
              new PathChangeModel(
                  tw.getPathString(),
                  tw.getPathString(),
                  0,
                  tw.getRawMode(0),
                  commit.getId().getName(),
                  ChangeType.ADD));
        }
        tw.release();
      } else {
        RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
        DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
        df.setRepository(repository);
        df.setDiffComparator(RawTextComparator.DEFAULT);
        df.setDetectRenames(true);
        List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree());
        for (DiffEntry diff : diffs) {
          if (diff.getChangeType().equals(ChangeType.DELETE)) {
            list.add(
                new PathChangeModel(
                    diff.getOldPath(),
                    diff.getOldPath(),
                    0,
                    diff.getNewMode().getBits(),
                    commit.getId().getName(),
                    diff.getChangeType()));
          } else {
            list.add(
                new PathChangeModel(
                    diff.getNewPath(),
                    diff.getNewPath(),
                    0,
                    diff.getNewMode().getBits(),
                    commit.getId().getName(),
                    diff.getChangeType()));
          }
        }
      }
    } catch (Throwable t) {
      // todo
      Logger.error(t, t.getMessage());
    } finally {
      rw.dispose();
    }
    return list;
  }
Ejemplo n.º 12
0
 private static boolean isRename(DiffEntry ent) {
   return ent.getChangeType() == ChangeType.RENAME || ent.getChangeType() == ChangeType.COPY;
 }
Ejemplo n.º 13
0
  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;
  }
Ejemplo n.º 14
0
  protected String doDiff(Git git, String objectId, String baseObjectId, String path) {
    Repository r = git.getRepository();
    /*
            RevCommit commit = JGitUtils.getCommit(r, objectId);

            ObjectId current;
            if (isNotBlank(objectId)) {
                current = BlobUtils.getId(r, objectId, blobPath);
            } else {
                current = CommitUtils.getHead(r).getId();
            }
            ObjectId previous;
            if (isNotBlank(baseObjectId)) {
                previous = BlobUtils.getId(r, baseObjectId, blobPath);
            } else {
                RevCommit revCommit = CommitUtils.getCommit(r, current);
                RevCommit[] parents = revCommit.getParents();
                if (parents.length == 0) {
                    throw new IllegalArgumentException("No parent commits!");
                } else {
                    previous = parents[0];
                }
            }
            Collection<Edit> changes = BlobUtils.diff(r, previous, current);

            // no idea how to format Collection<Edit> :)

    */

    RevCommit commit;
    if (Strings.isNotBlank(objectId)) {
      commit = CommitUtils.getCommit(r, objectId);
    } else {
      commit = CommitUtils.getHead(r);
    }
    RevCommit baseCommit = null;
    if (Strings.isNotBlank(baseObjectId)) {
      baseCommit = CommitUtils.getCommit(r, baseObjectId);
    }

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    RawTextComparator cmp = RawTextComparator.DEFAULT;
    DiffFormatter formatter = new DiffFormatter(buffer);
    formatter.setRepository(r);
    formatter.setDiffComparator(cmp);
    formatter.setDetectRenames(true);

    RevTree commitTree = commit.getTree();
    RevTree baseTree;
    try {
      if (baseCommit == null) {
        if (commit.getParentCount() > 0) {
          final RevWalk rw = new RevWalk(r);
          RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
          rw.dispose();
          baseTree = parent.getTree();
        } else {
          // FIXME initial commit. no parent?!
          baseTree = commitTree;
        }
      } else {
        baseTree = baseCommit.getTree();
      }

      List<DiffEntry> diffEntries = formatter.scan(baseTree, commitTree);
      if (path != null && path.length() > 0) {
        for (DiffEntry diffEntry : diffEntries) {
          if (diffEntry.getNewPath().equalsIgnoreCase(path)) {
            formatter.format(diffEntry);
            break;
          }
        }
      } else {
        formatter.format(diffEntries);
      }
      formatter.flush();
      return buffer.toString();
    } catch (IOException e) {
      throw new RuntimeIOException(e);
    }
  }
Ejemplo n.º 15
0
  /**
   * Returns the diff between two commits for the specified file.
   *
   * @param repository
   * @param baseCommit if base commit is null the diff is to the primary parent of the commit.
   * @param commit
   * @param path if the path is specified, the diff is restricted to that file or folder. if
   *     unspecified, the diff is for the entire commit.
   * @param outputType
   * @return the diff
   */
  public static DiffOutput getDiff(
      Repository repository,
      RevCommit baseCommit,
      RevCommit commit,
      String path,
      DiffOutputType outputType) {
    DiffStat stat = null;
    String diff = null;
    try {
      final ByteArrayOutputStream os = new ByteArrayOutputStream();
      RawTextComparator cmp = RawTextComparator.DEFAULT;
      DiffFormatter df;
      switch (outputType) {
        case HTML:
          df = new GitBlitDiffFormatter(os, commit.getName());
          break;
        case PLAIN:
        default:
          df = new DiffFormatter(os);
          break;
      }
      df.setRepository(repository);
      df.setDiffComparator(cmp);
      df.setDetectRenames(true);

      RevTree commitTree = commit.getTree();
      RevTree baseTree;
      if (baseCommit == null) {
        if (commit.getParentCount() > 0) {
          final RevWalk rw = new RevWalk(repository);
          RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
          rw.dispose();
          baseTree = parent.getTree();
        } else {
          // FIXME initial commit. no parent?!
          baseTree = commitTree;
        }
      } else {
        baseTree = baseCommit.getTree();
      }

      List<DiffEntry> diffEntries = df.scan(baseTree, commitTree);
      if (path != null && path.length() > 0) {
        for (DiffEntry diffEntry : diffEntries) {
          if (diffEntry.getNewPath().equalsIgnoreCase(path)) {
            df.format(diffEntry);
            break;
          }
        }
      } else {
        df.format(diffEntries);
      }
      if (df instanceof GitBlitDiffFormatter) {
        // workaround for complex private methods in DiffFormatter
        diff = ((GitBlitDiffFormatter) df).getHtml();
        stat = ((GitBlitDiffFormatter) df).getDiffStat();
      } else {
        diff = os.toString();
      }
      df.flush();
    } catch (Throwable t) {
      try {
        error(t, repository, "failed to generate commit diff!");
      } catch (Exception e) {
      }
    }

    return new DiffOutput(outputType, diff, stat);
  }
Ejemplo n.º 16
0
  public String getDiffText(String str) {
    int filenubs = 0;
    int lines = 0;
    FileRepositoryBuilder builder = new FileRepositoryBuilder();
    File gitDir = new File("F:/work/demo-rio/.git");
    Repository repository = null;
    try {
      if (git == null) {
        git = Git.open(gitDir);
      }
    } catch (Exception e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }
    try {
      String cid = str;
      repository = builder.setGitDir(gitDir).readEnvironment().findGitDir().build();
      RevWalk walk = new RevWalk(repository);
      ObjectId objId = ObjectId.fromString(cid);
      RevCommit commit = walk.parseCommit(objId);
      System.out.println(commit.getFullMessage());

      TreeWalk tw = new TreeWalk(repository);

      RevCommit[] parent_commits = commit.getParents();
      if (parent_commits.length == 0) {
        throw new Exception("当前只有一个版本");
      }

      ObjectId objId2 = parent_commits[0].toObjectId();
      RevCommit paren_commit = walk.parseCommit(objId2);
      tw.addTree(paren_commit.getTree());
      tw.addTree(commit.getTree());
      tw.setRecursive(true);

      ByteArrayOutputStream out = new ByteArrayOutputStream();
      DiffFormatter df = new DiffFormatter(out);
      df.setRepository(git.getRepository());
      RenameDetector rd = new RenameDetector(repository);
      rd.addAll(DiffEntry.scan(tw));
      List<DiffEntry> diffEntries = rd.compute();
      if (diffEntries != null || diffEntries.size() != 0) {

        Iterator<DiffEntry> iterator = new ArrayList<DiffEntry>(diffEntries).iterator();
        DiffEntry diffEntry = null;
        while (iterator.hasNext()) {
          diffEntry = iterator.next();
          String changeType = diffEntry.getChangeType().toString();
          String type = "";
          if (changeType.equals("DELETE")) {
            type = ConfigUtil.getFileType(diffEntry.getOldPath());
            filenubs++;
            System.out.println(diffEntry.getOldPath());
          } else {
            type = ConfigUtil.getFileType(diffEntry.getNewPath());
            filenubs++;
            System.out.println(diffEntry.getNewPath());
          }
          // 检查文件的后缀
          // System.out.println(type);
          if (fileTypes.contains(type)) {
            df.format(diffEntry);
            String diffText = out.toString("UTF-8");
            lines += scanDiffText(diffText);
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return filenubs + "&" + lines;
    // System.out.println("the changed file nubs: "+ filenubs);
    // System.out.println("the changed linr nubs: "+ lines);
  }