Skip to content

Repository files navigation

Jardiff

Maven Central GitHub release Build Status License

Jardiff is a command-line tool for comparing the contents of JAR files and directories. It provides detailed, line-based diffs of class files and resources, making it easy to spot changes between builds, releases, or different versions of Java/Kotlin projects.

Note

This project is not affiliated with Lightbend or the original jardiff project.

While lightbend-labs/jardiff and this tool share the same name and similar goals, they have different features:

The lightbend-labs/jardiff focuses on Scala projects, and it has some useful flags to tweak the bytecode output (ordering, suppress private members). This tool also has a mode to create a git repository to leverage git diff capabilities.

However, bric3/jardiff offers a more versatile tooling to inspect class differences

  • Additional to the usual diff output, it provides statistics (--stat) and status (--status) modes similar to git diff/status
  • Supports class extension coalescing, in case classes are renamed to other extensions like .bin, .clazz, etc.
  • Various text representation modes for class files
  • Flexible glob pattern filtering for including/excluding files
  • Offers limited support for non-binary text files

Example output in default mode:

--- foo/bar/qux/Baz.class
+++ foo/bar/qux/Baz.class
@@ -6,1548 +6,17 @@
   // access flags 0x9
   public static create()Lfoo/bar/ZuulMatcher;
     NEW foo/bar/ZuulMatcher
     DUP
-    LDC 15
+    LDC 3
     ANEWARRAY foo/bar/Zuul
     DUP
     LDC 0
     NEW foo/bar/Zuul
     DUP
-    LDC 14
-    ANEWARRAY java/lang/String
-    DUP
     LDC 0
-    LDC "foo.bar.qux.T:18"
-    AASTORE
-    DUP
-    LDC 1
-    LDC "foo.bar.qux.U:33"
-    AASTORE
-    DUP
-    LDC 2
-    LDC "foo.bar.qux.V:49"
-    AASTORE
-    DUP
-    LDC 3
-    LDC "foo.bar.qux.W:68"
-    AASTORE
-    DUP
 ...
+    LDC 2
     NEW foo/bar/Zuul
     DUP
     LDC 0
     ANEWARRAY java/lang/String

Or with the --status mode

D  foo/bar/qux/Zuul.class
 D foo/bar/qux/Zig.class
M  foo/bar/qux/Baz.class

Or with the --stat mode

 foo/bar/qux/Zuul.class | 42 ++++++++++++++++++++++++++++++++----------
 foo/bar/qux/Zig.class  |  1 -
 foo/bar/qux/Baz.class  | 34 ++++++++++++++++++++++------------
 3 files changed, 54 insertions(+), 23 deletions(-)

With class extension coalescing, paths with different left and right extensions use a Git-style brace form:

M  foo/bar/qux/Baz{.class => .classdata}

When output piped to delta, it looks like this:

jardiff with delta

Features

Other tools didn't have the feature I wanted, or they were impractical to use, so I made my own.

  • Compare JARs and directories recursively
  • Line-based diffs for each files
  • Class file comparison using different strategy to produce text
    • Auto-detects class files from their bytecode header, even without a .class extension
    • ASM's Textify (default)
    • Class outline (version, is kotlin/groovy class, synthetic or bridge members)
    • Class File Version only
  • Binary diff as sha-1 hashes
  • Include glob patterns (for the relative paths inside the jars/directories)
  • Exclude glob patterns (for the relative paths inside the jars/directories)
  • Optional class member order normalization
  • Supports --exit-code for CI/CD pipelines

Features planned for future releases... :

  • Ignoring debug information in class files (like line numbers, local variable names, etc.)
  • Append Koltin/Scala/Groovy detection to regular class text output
  • Replace ASM by the Class file API (Need JDK 24+)
  • Better terminal integration, ideas: pager support, colors configuration, auto-detection of delta, etc. (Might need FFM, need JDK 22+)

Usage

Caution

This tool needs a JDK11 to build and run. Example with mise

$ mise exec java@corretto-11 -- java -jar jardiff-0.1.0-SNAPSHOT.jar

Tip

The jar on the Github release is an executable jar, so you can run it directly with ./jardiff-0.1.0-SNAPSHOT.jar {left} {right} after downloading it (chmoding it executable as needed).

Build it ./gradlew build, then run it:

$ jardiff --help
Usage: jardiff [-hVv] [--exit-code] [--class-text-producer=<tool>]
               [--color=<when>] [-c=<extension>[,<extension>...]]... [-e=<glob>
               [,<glob>...]]... [-i=<glob>[,<glob>...]]... [--status | --stat]
               <left> <right>
Compares two JAR files or directories and reports differences.
      <left>           The JAR file or directory to compare.
      <right>          The JAR file or directory to compare.
  -c, --class-exts, --coalesce-classe-exts=<extension>[,<extension>...]
                       Coalesce class files with the given extensions, in
                       addition to the usual 'class', i.e. makes classes
                       named 'Foo.class' and 'Foo.bin' aliased to the same
                       file entry. Class files are auto-detected from their
                       bytecode header. Takes a comma separated list, e.g.
                       'classdata' or 'raw,bin,clazz'.
      --class-text-producer=<tool>
                       Tool used to produce class text, possible values:
                       asm-textifier, class-file-version, class-outline, jcod,
                       javap
                       The javap producer uses the JDK running jardiff,
                       so output can vary by JDK version/vendor and
                       requires a JDK containing jdk.jdeps.
                       Default: 'asm-textifier'
      --color=<when>   Control when to use color output:
                       always, auto, never
                       Default: 'auto'
  -e, --exclude=<glob>[,<glob>...]
                       Glob exclude patterns (comma separated), e.g.
                       '**/raw*/**', or '**/*.bin'.
      --exit-code      Make jardiff exit with codes similar to diff(1).
                       That is, it exits with 1 if there were differences
                       and 0 means no differences.
  -h, --help           Show this help message and exit.
      --ignore-member-order
                       Ignore class member declaration order when
                       comparing class files.
  -i, --include=<glob>[,<glob>...]
                       Glob include patterns (comma separated), e.g.
                       '**/raw*/**', or '**/*.bin'.
      --stat           Show statistics output (like 'git diff --stat').
                       Displays file-by-file statistics with
                         additions/deletions.
      --status         Show short status output (like 'git status --short').
                       Displays two-column XY status for changed files.
  -v                   Specify multiple -v options to increase verbosity.
                       For example, '-v -v' or '-vv'.
  -V, --version        Print version information and exit.

Tip

Use shell features, e.g. in Bash, ZSH instead of typing twice long folders use the brace expansion :

$ java -jar jardiff-0.1.0-SNAPSHOT.jar /Users/brice.dutheil/path/to/repositories/project{-original,-with-changes}/submodule/submodule/submodule/build/classes/java/main

Also, you can run it from Gradle:

$ ./gradlew run --args="{left} {right}"
  • {left} and {right} can be paths to JAR files or directories.
  • The tool outputs a summary and detailed diff of all differing files.

Building

To build the project:

$ ./gradlew build

Dependencies

  • Kotlin
  • Picocli
  • ASM
  • java-diff-utils
  • Byte Buddy
  • Apache Tika (for charset detection)

License

Copyright 2025 Brice Dutheil

Unless otherwise noted, all components are licenced under the Mozilla Public License Version 2.0.

About

Command line tool to compare classes in directories or jars

Topics

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages