@ -66,13 +66,40 @@ import org.eclipse.jgit.util.QuotedString;
public class DiffFormatter {
private static final byte [ ] noNewLine = encodeASCII ( "\\ No newline at end of file\n" ) ;
private final OutputStream out ;
private Repository db ;
private int context ;
/** Create a new formatter with a default level of context. */
public DiffFormatter ( ) {
/ * *
* Create a new formatter with a default level of context .
*
* @param out
* the stream the formatter will write line data to . This stream
* should have buffering arranged by the caller , as many small
* writes are performed to it .
* /
public DiffFormatter ( OutputStream out ) {
this . out = out ;
setContext ( 3 ) ;
}
/** @return the stream we are outputting data to. */
protected OutputStream getOutputStream ( ) {
return out ;
}
/ * *
* Set the repository the formatter can load object contents from .
*
* @param repository
* source repository holding referenced objects .
* /
public void setRepository ( Repository repository ) {
db = repository ;
}
/ * *
* Change the number of lines of context to display .
*
@ -83,45 +110,61 @@ public class DiffFormatter {
* /
public void setContext ( final int lineCount ) {
if ( lineCount < 0 )
throw new IllegalArgumentException ( JGitText . get ( ) . contextMustBeNonNegative ) ;
throw new IllegalArgumentException (
JGitText . get ( ) . contextMustBeNonNegative ) ;
context = lineCount ;
}
/ * *
* Flush the underlying output stream of this formatter .
*
* @throws IOException
* the stream ' s own flush method threw an exception .
* /
public void flush ( ) throws IOException {
out . flush ( ) ;
}
/ * *
* Format a patch script from a list of difference entries .
*
* @param out
* stream to write the patch script out to .
* @param src
* repository the file contents can be read from .
* @param entries
* entries describing the affected files .
* @throws IOException
* a file ' s content cannot be read , or the output stream cannot
* be written to .
* /
public void format ( final OutputStream out , Repository src ,
List < ? extends DiffEntry > entries ) throws IOException {
for ( DiffEntry ent : entries ) {
if ( ent instanceof FileHeader ) {
format (
out ,
( FileHeader ) ent , //
newRawText ( open ( src , ent . getOldMode ( ) , ent . getOldId ( ) ) ) ,
newRawText ( open ( src , ent . getNewMode ( ) , ent . getNewId ( ) ) ) ) ;
} else {
format ( out , src , ent ) ;
}
public void format ( List < ? extends DiffEntry > entries ) throws IOException {
for ( DiffEntry ent : entries )
format ( ent ) ;
}
/ * *
* Format a patch script for one file entry .
*
* @param entry
* the entry to be formatted .
* @throws IOException
* a file ' s content cannot be read , or the output stream cannot
* be written to .
* /
public void format ( DiffEntry entry ) throws IOException {
if ( entry instanceof FileHeader ) {
format (
( FileHeader ) entry , //
newRawText ( open ( entry . getOldMode ( ) , entry . getOldId ( ) ) ) ,
newRawText ( open ( entry . getNewMode ( ) , entry . getNewId ( ) ) ) ) ;
} else {
formatAndDiff ( entry ) ;
}
}
private void format ( OutputStream out , Repository src , DiffEntry ent )
throws IOException {
private void formatAndDiff ( DiffEntry ent ) throws IOException {
String oldName = quotePath ( "a/" + ent . getOldName ( ) ) ;
String newName = quotePath ( "b/" + ent . getNewName ( ) ) ;
out . write ( encode ( "diff --git " + oldName + " " + newName + "\n" ) ) ;
switch ( ent . getChangeType ( ) ) {
switch ( ent . getChangeType ( ) ) {
case ADD :
out . write ( encodeASCII ( "new file mode " ) ) ;
ent . getNewMode ( ) . copyTo ( out ) ;
@ -135,7 +178,7 @@ public class DiffFormatter {
break ;
case RENAME :
out . write ( encode ( "similarity index " + ent . getScore ( ) + "%" ) ) ;
out . write ( encodeASCII ( "similarity index " + ent . getScore ( ) + "%" ) ) ;
out . write ( '\n' ) ;
out . write ( encode ( "rename from " + quotePath ( ent . getOldName ( ) ) ) ) ;
@ -146,7 +189,7 @@ public class DiffFormatter {
break ;
case COPY :
out . write ( encode ( "similarity index " + ent . getScore ( ) + "%" ) ) ;
out . write ( encodeASCII ( "similarity index " + ent . getScore ( ) + "%" ) ) ;
out . write ( '\n' ) ;
out . write ( encode ( "copy from " + quotePath ( ent . getOldName ( ) ) ) ) ;
@ -178,9 +221,9 @@ public class DiffFormatter {
}
out . write ( encodeASCII ( "index " //
+ format ( src , ent . getOldId ( ) ) //
+ format ( ent . getOldId ( ) ) //
+ ".." //
+ format ( src , ent . getNewId ( ) ) ) ) ;
+ format ( ent . getNewId ( ) ) ) ) ;
if ( ent . getOldMode ( ) . equals ( ent . getNewMode ( ) ) ) {
out . write ( ' ' ) ;
ent . getNewMode ( ) . copyTo ( out ) ;
@ -189,8 +232,8 @@ public class DiffFormatter {
out . write ( encode ( "--- " + oldName + '\n' ) ) ;
out . write ( encode ( "+++ " + newName + '\n' ) ) ;
byte [ ] aRaw = open ( src , ent . getOldMode ( ) , ent . getOldId ( ) ) ;
byte [ ] bRaw = open ( src , ent . getNewMode ( ) , ent . getNewId ( ) ) ;
byte [ ] aRaw = open ( ent . getOldMode ( ) , ent . getOldId ( ) ) ;
byte [ ] bRaw = open ( ent . getNewMode ( ) , ent . getNewId ( ) ) ;
if ( RawText . isBinary ( aRaw ) | | RawText . isBinary ( bRaw ) ) {
out . write ( encodeASCII ( "Binary files differ\n" ) ) ;
@ -198,7 +241,7 @@ public class DiffFormatter {
} else {
RawText a = newRawText ( aRaw ) ;
RawText b = newRawText ( bRaw ) ;
formatEdits ( out , a , b , new MyersDiff ( a , b ) . getEdits ( ) ) ;
formatEdits ( a , b , new MyersDiff ( a , b ) . getEdits ( ) ) ;
}
}
@ -213,8 +256,8 @@ public class DiffFormatter {
return new RawText ( content ) ;
}
private String format ( Repository db , AbbreviatedObjectId oldId ) {
if ( oldId . isComplete ( ) )
private String format ( AbbreviatedObjectId oldId ) {
if ( oldId . isComplete ( ) & & db ! = null )
oldId = oldId . toObjectId ( ) . abbreviate ( db , 8 ) ;
return oldId . name ( ) ;
}
@ -224,7 +267,7 @@ public class DiffFormatter {
return ( '"' + name + '"' ) . equals ( q ) ? name : q ;
}
private byte [ ] open ( Repository src , FileMode mode , AbbreviatedObjectId id )
private byte [ ] open ( FileMode mode , AbbreviatedObjectId id )
throws IOException {
if ( mode = = FileMode . MISSING )
return new byte [ ] { } ;
@ -232,8 +275,10 @@ public class DiffFormatter {
if ( mode . getObjectType ( ) ! = Constants . OBJ_BLOB )
return new byte [ ] { } ;
if ( db = = null )
throw new IllegalStateException ( JGitText . get ( ) . repositoryIsRequired ) ;
if ( id . isComplete ( ) ) {
ObjectLoader ldr = src . openObject ( id . toObjectId ( ) ) ;
ObjectLoader ldr = db . openObject ( id . toObjectId ( ) ) ;
return ldr . getCachedBytes ( ) ;
}
@ -247,8 +292,6 @@ public class DiffFormatter {
* to increase or reduce the number of lines of context within the script .
* All header lines are reused as - is from the supplied FileHeader .
*
* @param out
* stream to write the patch script out to .
* @param head
* existing file header containing the header lines to copy .
* @param a
@ -260,8 +303,8 @@ public class DiffFormatter {
* @throws IOException
* writing to the supplied stream failed .
* /
public void format ( final OutputStream out , final FileHeader head ,
final RawText a , final RawText b ) throws IOException {
public void format ( final FileHeader head , final RawText a , final RawText b )
throws IOException {
// Reuse the existing FileHeader as-is by blindly copying its
// header lines, but avoiding its hunks. Instead we recreate
// the hunks from the text instances we have been supplied.
@ -272,19 +315,22 @@ public class DiffFormatter {
end = head . getHunks ( ) . get ( 0 ) . getStartOffset ( ) ;
out . write ( head . getBuffer ( ) , start , end - start ) ;
formatEdits ( out , a , b , head . toEditList ( ) ) ;
formatEdits ( a , b , head . toEditList ( ) ) ;
}
/ * *
* Formats a list of edits in unified diff format
* @param out where the unified diff is written to
* @param a the text A which was compared
* @param b the text B which was compared
* @param edits some differences which have been calculated between A and B
*
* @param a
* the text A which was compared
* @param b
* the text B which was compared
* @param edits
* some differences which have been calculated between A and B
* @throws IOException
* /
public void formatEdits ( final OutputStream out , final RawText a ,
final RawText b , final EditList edits ) throws IOException {
public void formatEdits ( final RawText a , final RawText b ,
final EditList edits ) throws IOException {
for ( int curIdx = 0 ; curIdx < edits . size ( ) ; ) {
Edit curEdit = edits . get ( curIdx ) ;
final int endIdx = findCombinedEnd ( edits , curIdx ) ;
@ -295,18 +341,24 @@ public class DiffFormatter {
final int aEnd = Math . min ( a . size ( ) , endEdit . getEndA ( ) + context ) ;
final int bEnd = Math . min ( b . size ( ) , endEdit . getEndB ( ) + context ) ;
writeHunkHeader ( out , aCur , aEnd , bCur , bEnd ) ;
writeHunkHeader ( aCur , aEnd , bCur , bEnd ) ;
while ( aCur < aEnd | | bCur < bEnd ) {
if ( aCur < curEdit . getBeginA ( ) | | endIdx + 1 < curIdx ) {
writeContextLine ( out , a , aCur , isEndOfLineMissing ( a , aCur ) ) ;
writeContextLine ( a , aCur ) ;
if ( isEndOfLineMissing ( a , aCur ) )
out . write ( noNewLine ) ;
aCur + + ;
bCur + + ;
} else if ( aCur < curEdit . getEndA ( ) ) {
writeRemovedLine ( out , a , aCur , isEndOfLineMissing ( a , aCur ) ) ;
writeRemovedLine ( a , aCur ) ;
if ( isEndOfLineMissing ( a , aCur ) )
out . write ( noNewLine ) ;
aCur + + ;
} else if ( bCur < curEdit . getEndB ( ) ) {
writeAddedLine ( out , b , bCur , isEndOfLineMissing ( b , bCur ) ) ;
writeAddedLine ( b , bCur ) ;
if ( isEndOfLineMissing ( b , bCur ) )
out . write ( noNewLine ) ;
bCur + + ;
}
@ -317,21 +369,17 @@ public class DiffFormatter {
}
/ * *
* Output a line of diff context
* Output a line of context ( unmodified line ) .
*
* @param out
* OutputStream
* @param text
* RawText for accessing raw data
* @param line
* the line number within text
* @param endOfLineMissing
* true if we should add the GNU end of line missing warning
* @throws IOException
* /
protected void writeContextLine ( final OutputStream out , final RawText text ,
final int line , boolean endOfLineMissing ) throws IOException {
writeLine ( out , ' ' , text , line , endOfLineMissing ) ;
protected void writeContextLine ( final RawText text , final int line )
throws IOException {
writeLine ( ' ' , text , line ) ;
}
private boolean isEndOfLineMissing ( final RawText text , final int line ) {
@ -339,46 +387,36 @@ public class DiffFormatter {
}
/ * *
* Output an added line
* Output an added line .
*
* @param out
* OutputStream
* @param text
* RawText for accessing raw data
* @param line
* the line number within text
* @param endOfLineMissing
* true if we should add the gnu end of line missing warning
* @throws IOException
* /
protected void writeAddedLine ( final OutputStream out , final RawText text , final int line , boolean endOfLineMissing )
protected void writeAddedLine ( final RawText text , final int line )
throws IOException {
writeLine ( out , '+' , text , line , endOfLineMissing ) ;
writeLine ( '+' , text , line ) ;
}
/ * *
* Output a removed line
*
* @param out
* OutputStream
* @param text
* RawText for accessing raw data
* @param line
* the line number within text
* @param endOfLineMissing
* true if we should add the gnu end of line missing warning
* @throws IOException
* /
protected void writeRemovedLine ( final OutputStream out , final RawText text ,
final int line , boolean endOfLineMissing ) throws IOException {
writeLine ( out , '-' , text , line , endOfLineMissing ) ;
protected void writeRemovedLine ( final RawText text , final int line )
throws IOException {
writeLine ( '-' , text , line ) ;
}
/ * *
* Output a hunk header
*
* @param out
* OutputStream
* @param aStartLine
* within first source
* @param aEndLine
@ -389,20 +427,20 @@ public class DiffFormatter {
* within second source
* @throws IOException
* /
protected void writeHunkHeader ( final OutputStream out , int aStartLine , int aEndLine ,
protected void writeHunkHeader ( int aStartLine , int aEndLine ,
int bStartLine , int bEndLine ) throws IOException {
out . write ( '@' ) ;
out . write ( '@' ) ;
writeRange ( out , '-' , aStartLine + 1 , aEndLine - aStartLine ) ;
writeRange ( out , '+' , bStartLine + 1 , bEndLine - bStartLine ) ;
writeRange ( '-' , aStartLine + 1 , aEndLine - aStartLine ) ;
writeRange ( '+' , bStartLine + 1 , bEndLine - bStartLine ) ;
out . write ( ' ' ) ;
out . write ( '@' ) ;
out . write ( '@' ) ;
out . write ( '\n' ) ;
}
private static void writeRange ( final OutputStream out , final char prefix ,
final int begin , final int cnt ) throws IOException {
private void writeRange ( final char prefix , final int begin , final int cnt )
throws IOException {
out . write ( ' ' ) ;
out . write ( prefix ) ;
switch ( cnt ) {
@ -431,18 +469,23 @@ public class DiffFormatter {
}
}
private static void writeLine ( final OutputStream out , final char prefix ,
final RawText text , final int cur , boolean noNewLineIndicator ) throws IOException {
/ * *
* Write a standard patch script line .
*
* @param prefix
* prefix before the line , typically '-' , '+' , ' ' .
* @param text
* the text object to obtain the line from .
* @param cur
* line number to output .
* @throws IOException
* the stream threw an exception while writing to it .
* /
protected void writeLine ( final char prefix , final RawText text ,
final int cur ) throws IOException {
out . write ( prefix ) ;
text . writeLine ( out , cur ) ;
out . write ( '\n' ) ;
if ( noNewLineIndicator )
writeNoNewLine ( out ) ;
}
private static void writeNoNewLine ( final OutputStream out )
throws IOException {
out . write ( noNewLine ) ;
}
private int findCombinedEnd ( final List < Edit > edits , final int i ) {