8

私は無料の(オープンソースの)Java差分ライブラリのGoogleの結果を調べていますが、かなりの数のライブラリがあるようです(文字列だけでなくジェネリックオブジェクトでも動作するものもあります)。

大量の検索結果を調べて、検索対象が見つからない前に、まずここで質問します。

これらの差分ライブラリのいずれかが、cvsannotateやsvnblameなどの機能をサポートしていますか?したい

  • 電流String[]を関数に渡す
  • 古いバージョンのをString[]関数に渡し続けると、それらをすべて使い切るか、ライブラリから元の行に注釈が付けられていないことが示されます(最後のことは必須ではありませんが、古いバージョンを取得するので非常に便利です。String[]高いのでできるだけ早くやめたいです)
  • ìnt[]現在のバージョンのすべての行、最後に変更されたバージョン、またはまったく変更されていない(つまり、最初のバージョンで最後に変更された)かどうかを通知する関数を呼び出します。

sではないオブジェクトをサポートすることStringは素晴らしいことですが、必須ではありません。そして、APIが正確にそのようになっていない場合、私はそれと一緒に暮らすことができると思います。

ない場合は、その機能を簡単に追加できる拡張可能なdiffライブラリを提案できますか?できれば、その機能を投稿として受け取りたいものです(そして、投稿を受け入れる前に大量の書類を記入する必要はありません。 GNUプロジェクト)?それなら、私はそれをそこに追加することを志願します(少なくとも試みます)。

4

3 に答える 3

2

DmitryNaumenkoのjava-diff-utilsライブラリに自分で実装することにしました。

/*
   Copyright 2010 Michael Schierl (schierlm@gmx.de)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package difflib.annotate;

import java.util.*;

import difflib.*;

/**
 * Generates an annotated version of a revision based on a list of older
 * revisions, like <tt>cvs annotate</tt> or <tt>svn blame</tt>.
 * 
 * @author <a href="schierlm@gmx.de">Michael Schierl</a>
 * 
 * @param <R>
 *            Type of the revision metadata
 */
public class Annotate<R> {

    private final List<R> revisions;
    private final int[] lineNumbers;
    private R currentRevision;
    private final List<Object> currentLines;
    private final List<Integer> currentLineMap;

    /**
     * Creates a new annotation generator.
     * 
     * @param revision
     *            Revision metadata for the revision to be annotated
     * @param targetLines
     *            Lines of the revision to be annotated
     */
    public Annotate(R revision, List<?> targetLines) {
        revisions = new ArrayList<R>();
        lineNumbers = new int[targetLines.size()];
        currentRevision = revision;
        currentLines = new ArrayList<Object>(targetLines);
        currentLineMap = new ArrayList<Integer>();
        for (int i = 0; i < lineNumbers.length; i++) {
            lineNumbers[i] = -1;
            revisions.add(null);
            currentLineMap.add(i);
        }
    }

    /**
     * Check whether there are still lines that are unannotated. In that case,
     * more older revisions should be retrieved and passed to the function. Note
     * that as soon as you pass an empty revision, all lines will be annotated
     * (with a later revision), therefore if you do not have any more revisions,
     * pass an empty revision to annotate the rest of the lines.
     */
    public boolean areLinesUnannotated() {
        for (int i = 0; i < lineNumbers.length; i++) {
            if (lineNumbers[i] == -1 || revisions.get(i) == null)
                return true;
        }
        return false;
    }

    /**
     * Add the previous revision and update annotation info.
     * 
     * @param revision
     *            Revision metadata for this revision
     * @param lines
     *            Lines of this revision
     */
    public void addRevision(R revision, List<?> lines) {
        Patch patch = DiffUtils.diff(currentLines, lines);
        int lineOffset = 0; // remember number of already deleted/added lines
        for (Delta d : patch.getDeltas()) {
            Chunk original = d.getOriginal();
            Chunk revised = d.getRevised();
            int pos = original.getPosition() + lineOffset;
            // delete lines
            for (int i = 0; i < original.getSize(); i++) {
                int origLine = currentLineMap.remove(pos);
                currentLines.remove(pos);
                if (origLine != -1) {
                    lineNumbers[origLine] = original.getPosition() + i;
                    revisions.set(origLine, currentRevision);
                }
            }
            for (int i = 0; i < revised.getSize(); i++) {
                currentLines.add(pos + i, revised.getLines().get(i));
                currentLineMap.add(pos + i, -1);
            }
            lineOffset += revised.getSize() - original.getSize();
        }

        currentRevision = revision;
        if (!currentLines.equals(lines))
            throw new RuntimeException("Patch application failed");
    }

    /**
     * Return the result of the annotation. It will be a List of the same length
     * as the target revision, for which every entry states the revision where
     * the line appeared last.
     */
    public List<R> getAnnotatedRevisions() {
        return Collections.unmodifiableList(revisions);
    }

    /**
     * Return the result of the annotation. It will be a List of the same length
     * as the target revision, for which every entry states the line number in
     * the revision where the line appeared last.
     */
    public int[] getAnnotatedLineNumbers() {
        return (int[]) lineNumbers.clone();
    }
}

彼がそれを含めたい場合に備えて、私はそれをドミトリー・ナウメンコ(いくつかのテストケースを含む)にも送りました。

于 2010-12-27T15:22:33.347 に答える
1

私は間違っているかもしれませんが、ファイルの履歴にアクセスする必要があるため、注釈/非難が機能するにはバージョン管理システムが必要だと思います。一般的なdiffライブラリはそれを行うことができません。したがって、これがターゲットである場合は、svnkitなどのVCSで動作するライブラリを確認してください。そうでない場合、そのようなライブラリは、注釈/非難がどのように行われるかについての良い出発点になる可能性があります。これには、ファイルのすべてのバージョンのチェーンを比較することが含まれることがよくあります。

于 2010-12-23T14:10:05.897 に答える
1

xwiki-commons-blame-apiを使用できます 。実際には、このスレッドで受け入れられた回答のコードを使用しています(StackOverflowでこのコードを共有してくれたMichael Schierlに感謝します

ユニットテストでJavaでの使用方法を確認できます。

またはScalaでは:

import java.util
import org.xwiki.blame.AnnotatedContent
import org.xwiki.blame.internal.DefaultBlameManager

case class Revision(id: Int,
                    parentId: Option[Int] = None,
                    content: Option[String] = None)

def createAnnotation(revisions: Seq[Revision]): Option[AnnotatedContent[Revision, String]] = {
    val blameManager = new DefaultBlameManager()

    val annotatedContent = revisions.foldLeft(null.asInstanceOf[AnnotatedContent[Revision, String]]){
      (annotation, revision) =>
        blameManager.blame(annotation, revision, splitByWords(revision.content))
    }
    Option(annotatedContent)
}

def splitByWords(content: Option[String]): util.List[String] = {
    val array = content.fold(Array[String]())(_.split("[^\\pL_\\pN]+"))
    util.Arrays.asList(array:_*)
}
于 2015-02-21T17:32:14.157 に答える