# dependency-tree-diff: seeing which root dependency a Gradle version bump came from

> A Kotlin command line tool that diffs two Gradle dependencies task outputs and keeps only the minimal unchanged parent lines needed to show where a change entered the tree, so reviewers can trace a transitive bump back to its root.

**JakeWharton/dependency-tree-diff** — An intelligent diff tool for the output of Gradle's dependencies task

- Repository: https://github.com/JakeWharton/dependency-tree-diff
- Stars: 839 · Forks: 24
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/jakewharton-dependency-tree-diff

## Gradle's dependency tree buries the origin of a version bump

The Gradle dependencies task prints a tree, and every line repeats the path from the root. That repetition makes the tree readable for a human browsing it, but it makes it nearly useless as diff input. When a build script bumps one library, the resolved version of that library can change at several places in the tree at once, and a plain diff of the two outputs shows scattered line pairs with no indication of which direct dependency caused the change. The reader has to reconstruct the tree mentally to answer the only question that matters: what did I just pull in.

dependency-tree-diff exists for exactly that gap. It is a small Kotlin program that takes two text files holding Gradle dependencies task output and prints a diff in which unchanged entries are shown only when they are a parent of something that changed. Everything else that did not change and is not on the path to a change is dropped. The result is a diff where every hunk already contains the root that explains it.

The audience is narrow and specific: engineers reviewing dependency changes in a Gradle project, usually through a pull request, who need to see the effect of a version change rather than the full resolved graph.

## The unchanged-parent rule is the whole mechanism

The distinguishing behaviour is that unchanged entries appear only when they are a parent dependency to one that changed. A normal diff prints every context line it is configured to keep, which is why the project's documentation contrasts the output with `diff -U 0` and with `diff -U 2`. With zero context, the changed lines float with no context as to what is affected. With two lines of context, some roots become visible but unrelated unchanged dependencies, such as the androidx.sqlite entries and the SQLDelight runtime pulled in by the RxJava 2 extensions artifact, start appearing too.

For deep dependency chains, raising the context count only adds more unchanged lines. The project states the rule directly: dependency-tree-diff always displays the minimal subset needed to provide context. In practice that means the tool has to understand the tree structure of the Gradle output, track the parent chain of each changed node, and re-emit that chain.

Because the parents are retained and the siblings are not, the printed diff stays proportional to the number of changes rather than to the size of the dependency graph. The published example shows a single Kotlin standard library bump from 1.3.72 to 1.4.0 appearing at four places in the tree, with the unchanged androidx.sqlite and SQLDelight runtime lines still visible in the context because they sit above a changed line.

## Capturing two trees with one configuration

The tool does not run Gradle itself. It parses output you capture yourself, so the first real use is producing two comparable files. The usage sequence is short: dump the dependencies of a single configuration to old.txt, make the dependency change, dump it again to new.txt, then run the JAR over the two files. Passing --configuration is what keeps each file to one tree instead of the several trees Gradle prints for a full build. That flag matters, because a diff across mixed configurations would compare unrelated trees.

```bash
$ ./gradlew :app:dependencies --configuration releaseRuntimeClasspath > old.txt
$ # Update a dependency...
$ ./gradlew :app:dependencies --configuration releaseRuntimeClasspath > new.txt
$ ./dependency-tree-diff.jar old.txt new.txt
```

What you should see is a diff that names the changed coordinates and keeps the minimum set of parent lines needed to show where each change entered the tree. The example fixture set under src/test/fixtures/ contains example outputs together with their expected diffs, which is the place to look when you want to confirm the tool behaves as documented before pointing it at your own build.

## Getting the JAR outside Homebrew

Installation is deliberately plain because the project ships as a standalone JAR with no runtime to configure. On macOS the install instructions point at Homebrew:

```bash
$ brew install JakeWharton/repo/dependency-tree-diff
```

On other systems the instructions are to download the standalone JAR from the latest release page. On Linux and macOS you can `chmod +x` and execute the `.jar` directly, and on Windows you run it with `java -jar`. The project describes itself as an intelligent diff tool for the output of Gradle's dependencies task, and its repository is written in Kotlin, but there is no Gradle build step, plugin, or library API described for consuming it in a build script. It is a developer tool you run by hand or wire into a script.

Because it takes plain text files as arguments, the tool also fits a CI job that archives dependency output per commit and compares two of them later, without changing the build itself.

## Where a plain diff -U 2 stops being enough

The tool is a wrapper around text comparison, so it inherits the assumptions of the Gradle tree format. It parses the output of the dependencies task, which means the input has to be that task's output, produced with a single configuration. If you feed it unrelated text, or a dependencies dump without the --configuration flag that limits it to one tree, the parent relationships the tool relies on are not the ones it needs.

There is a second limitation visible in the project's own example: to reach the root dependencies by hand you need `diff -U 2`, and the documentation notes that for deep dependency chains -U 2 will not be enough to see all the roots. Raising the value shows more unchanged dependencies, not more roots. dependency-tree-diff always shows the minimal subset, so the manual workaround is worse in both directions, and the tool answers a real problem rather than a nicer formatting of a solved one.

The tool also does not extend past text. It does not resolve artifacts, does not download or verify checksums, does not report licences, and does not scan for known vulnerabilities. Anything of that kind is out of its scope.

## Apache-2.0 and the gap between the last push and the last release

The project is published under the Apache License 2.0, with the copyright held by Jake Wharton. The licence text ships in the repository as LICENSE.txt, and the standard Apache 2.0 grant is reproduced in full there. For teams running the JAR inside a build pipeline, that matters because it is a permissive licence with no copyleft obligation on the surrounding build scripts, though it does require that the licence and notices travel with redistributed copies.

On maintenance, the repository is not archived and the last push was on 2026-10-15. The most recent tagged release is 1.2.1 dated 2024-01-02, preceded by 1.2.0 on 2020-09-16 and 1.1.0 on 2020-08-25. Code has therefore landed in the repository well after the last published release tag, and the commit list does not say what those changes contain. The upgrade cost for a new user is simply downloading the standalone JAR from the latest release page, which at present is 1.2.1.

The top level of the repository also holds CHANGELOG.md and RELEASING.md, which is where a team would look to understand what a given tag contains.

## Where the tool earns its place in a review

The scenario the project is built for is a pull request that changes a dependency declaration. Without a tool like this, a reviewer sees a wall of coordinate lines and has to work out which direct dependency pulled in the new transitive version. With it, the diff opens at the root of each affected subtree, so the reviewer can see that the Kotlin standard library moved from 1.3.72 to 1.4.0 under the SQLDelight android driver and under the RxJava 2 extensions artifact, and can decide whether that was intended.

This makes it a reviewing aid rather than an analysis tool. It does not answer whether the new version is safe, whether it is compatible with your code, or what the transitive closure looks like in total. Those questions need a different tool, and expecting this one to answer them will lead to disappointment.

For teams on Maven rather than Gradle, it is simply the wrong program: the parser expects the Gradle tree format, and the project is framed entirely around the dependencies task. Teams that use Gradle but need resolved artifacts, licence inventories, or vulnerability reports should keep those separate workflows in place and use this one only to make the review diff readable.

## Conclusion

Adopt dependency-tree-diff if your team reviews Gradle dependency changes in pull requests and needs the origin of a transitive bump made visible. Skip it if you need resolved artifacts, license or vulnerability reporting, or if you would rather read a dependency list than a tree. Before the first run, check that both text files were captured with the same --configuration value and verify the fixture expectations under src/test/fixtures/ against your own layout.

## FAQ

### How do I install dependency-tree-diff on macOS?

The install instructions give a Homebrew command, `brew install JakeWharton/repo/dependency-tree-diff`. On Linux, macOS and Windows you can also download the standalone JAR from the latest release page, running it directly after `chmod +x` on Linux and macOS, or with `java -jar` on Windows.

### What input does dependency-tree-diff expect?

It parses the output of Gradle's dependencies task, so you capture that output to a file first. The usage is to capture it with the --configuration flag so that only a single tree is shown, then pass the two files to the JAR.

### How is dependency-tree-diff different from diff -U 2?

A plain diff keeps unchanged lines only up to the configured context count, so for deep chains it shows extra unchanged dependencies and can still miss the roots. dependency-tree-diff always prints the minimal subset of unchanged entries needed to provide context, showing them only when they are a parent of something that changed.

### Can dependency-tree-diff tell me about licences or vulnerable versions?

No. The tool only compares two dependency tree texts and prints a diff. Licence or vulnerability analysis is not part of what the project does, and it does not resolve or download artifacts.

## Sources

- [Issues](https://github.com/JakeWharton/dependency-tree-diff/issues)
- [JakeWharton/dependency-tree-diff on GitHub](https://github.com/JakeWharton/dependency-tree-diff)
- [License: Apache-2.0](https://github.com/JakeWharton/dependency-tree-diff/blob/trunk/LICENSE)
- [README](https://github.com/JakeWharton/dependency-tree-diff/blob/trunk/README.md)
- [Releases](https://github.com/JakeWharton/dependency-tree-diff/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jakewharton-dependency-tree-diff
