Skip to content

Commit 989abbe

Browse files
authored
Merge pull request #631 from diffplug/feat/merge-parent
Base `ratchetFrom` on the merge base
2 parents 08340a1 + c0c9da0 commit 989abbe

File tree

5 files changed

+151
-7
lines changed

5 files changed

+151
-7
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1414
* `LineEnding.GIT_ATTRIBUTES` now creates a policy whose serialized state can be relocated from one machine to another. No user-visible change, but paves the way for remote build cache support in Gradle. ([#621](https://github.com/diffplug/spotless/pull/621))
1515
### Added
1616
* `prettier` will now autodetect the parser (and formatter) to use based on the filename, unless you override this using `config` or `configFile` with the option `parser` or `filepath`. ([#620](https://github.com/diffplug/spotless/pull/620))
17+
* `GitRatchet` now lives in `lib-extra`, and is shared across `plugin-gradle` and `plugin-maven` ([#626](https://github.com/diffplug/spotless/pull/626)).
1718
### Changed
1819
* **BREAKING** `FileSignature` can no longer sign folders, only files. Signatures are now based only on filename (not path), size, and a content hash. It throws an error if a signature is attempted on a folder or on multiple files with different paths but the same filename - it never breaks silently. This change does not break any of Spotless' internal logic, so it is unlikely to affect any of Spotless' consumers either. ([#571](https://github.com/diffplug/spotless/pull/571))
1920
* This change allows the maven plugin to cache classloaders across subprojects when loading config resources from the classpath (fixes [#559](https://github.com/diffplug/spotless/issues/559)).

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ VER_SLF4J=[1.6,2.0[
2222

2323
# Used in multiple places
2424
VER_DURIAN=1.2.0
25-
VER_JGIT=5.7.0.202003110725-r
25+
VER_JGIT=5.8.0.202006091008-r
2626
VER_JUNIT=4.13
2727
VER_ASSERTJ=3.15.0
2828
VER_MOCKITO=3.3.3

lib-extra/src/main/java/com/diffplug/spotless/extra/GitRatchet.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.HashMap;
2121
import java.util.Map;
2222
import java.util.Objects;
23+
import java.util.Optional;
2324

2425
import javax.annotation.Nullable;
2526

@@ -31,6 +32,7 @@
3132
import org.eclipse.jgit.lib.RepositoryCache;
3233
import org.eclipse.jgit.revwalk.RevCommit;
3334
import org.eclipse.jgit.revwalk.RevWalk;
35+
import org.eclipse.jgit.revwalk.filter.RevFilter;
3436
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
3537
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
3638
import org.eclipse.jgit.treewalk.FileTreeIterator;
@@ -195,13 +197,21 @@ public synchronized ObjectId rootTreeShaOf(Project project, String reference) {
195197
Repository repo = repositoryFor(project);
196198
ObjectId treeSha = rootTreeShaCache.get(repo, reference);
197199
if (treeSha == null) {
198-
ObjectId commitSha = repo.resolve(reference);
199-
if (commitSha == null) {
200-
throw new IllegalArgumentException("No such reference '" + reference + "'");
201-
}
202200
try (RevWalk revWalk = new RevWalk(repo)) {
203-
RevCommit revCommit = revWalk.parseCommit(commitSha);
204-
treeSha = revCommit.getTree();
201+
ObjectId commitSha = repo.resolve(reference);
202+
if (commitSha == null) {
203+
throw new IllegalArgumentException("No such reference '" + reference + "'");
204+
}
205+
206+
RevCommit ratchetFrom = revWalk.parseCommit(commitSha);
207+
RevCommit head = revWalk.parseCommit(repo.resolve(Constants.HEAD));
208+
209+
revWalk.setRevFilter(RevFilter.MERGE_BASE);
210+
revWalk.markStart(ratchetFrom);
211+
revWalk.markStart(head);
212+
213+
RevCommit mergeBase = revWalk.next();
214+
treeSha = Optional.ofNullable(mergeBase).orElse(ratchetFrom).getTree();
205215
}
206216
rootTreeShaCache.put(repo, reference, treeSha);
207217
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2020 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.extra;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
23+
import org.eclipse.jgit.api.Git;
24+
import org.eclipse.jgit.api.errors.GitAPIException;
25+
import org.eclipse.jgit.lib.Constants;
26+
import org.eclipse.jgit.lib.ObjectId;
27+
import org.eclipse.jgit.lib.RefDatabase;
28+
import org.junit.Test;
29+
30+
import com.diffplug.spotless.ResourceHarness;
31+
32+
public class GitRachetMergeBaseTest extends ResourceHarness {
33+
@Test
34+
public void test() throws IllegalStateException, GitAPIException, IOException {
35+
try (Git git = initRepo()) {
36+
setFile("mine.txt").toContent("init");
37+
setFile("untouched.txt").toContent("init");
38+
addAndCommit(git, "init");
39+
40+
git.checkout().setCreateBranch(true).setName("rem-branch").call();
41+
// so at the point where we start work, we are clean relative to the remote
42+
ratchetFrom("main", "rem-branch").allClean();
43+
44+
// but when the remote changes
45+
setFile("untouched.txt").toContent("changed");
46+
addAndCommit(git, "remote");
47+
48+
// it shouldn't affect files that we haven't changed
49+
git.checkout().setName("main").call();
50+
ratchetFrom("main", "rem-branch").allClean();
51+
52+
// and when we work, it should continue to only affect our own work
53+
setFile("mine.txt").toContent("changed");
54+
ratchetFrom("main", "rem-branch").onlyDirty("mine.txt");
55+
}
56+
}
57+
58+
static class GitRatchetSimple extends GitRatchet<File> {
59+
@Override
60+
protected File getDir(File project) {
61+
return project;
62+
}
63+
64+
@Override
65+
protected File getParent(File project) {
66+
return project.getParentFile();
67+
}
68+
}
69+
70+
Asserter ratchetFrom(String... ratchetFroms) {
71+
return new Asserter(ratchetFroms);
72+
}
73+
74+
class Asserter {
75+
final GitRatchetSimple ratchet = new GitRatchetSimple();
76+
final String[] ratchetFroms;
77+
final ObjectId[] shas;
78+
79+
Asserter(String... ratchetFrom) {
80+
this.ratchetFroms = ratchetFrom;
81+
this.shas = Arrays.stream(ratchetFrom)
82+
.map(from -> ratchet.rootTreeShaOf(rootFolder(), from))
83+
.toArray(ObjectId[]::new);
84+
}
85+
86+
private void assertClean(int i, String filename, boolean expected) throws IOException {
87+
boolean actual = ratchet.isClean(rootFolder(), shas[i], newFile(filename));
88+
if (actual != expected) {
89+
throw new AssertionError("Expected " + filename + " to be " + (expected ? "clean" : "dirty") + " relative to " + ratchetFroms[i]);
90+
}
91+
}
92+
93+
public void allClean() throws IOException {
94+
onlyDirty();
95+
}
96+
97+
public void allDirty() throws IOException {
98+
String[] filenames = Arrays.stream(rootFolder().listFiles())
99+
.filter(File::isFile)
100+
.map(File::getName)
101+
.toArray(String[]::new);
102+
onlyDirty(filenames);
103+
}
104+
105+
public void onlyDirty(String... filenames) throws IOException {
106+
List<String> dirtyFiles = Arrays.asList(filenames);
107+
for (File file : rootFolder().listFiles()) {
108+
if (!file.isFile()) {
109+
continue;
110+
}
111+
boolean expectedClean = !dirtyFiles.contains(file.getName());
112+
for (int i = 0; i < shas.length; ++i) {
113+
assertClean(i, file.getName(), expectedClean);
114+
}
115+
}
116+
}
117+
}
118+
119+
private Git initRepo() throws IllegalStateException, GitAPIException, IOException {
120+
Git git = Git.init().setDirectory(rootFolder()).call();
121+
RefDatabase refDB = git.getRepository().getRefDatabase();
122+
refDB.newUpdate(Constants.R_HEADS + "main", false).setNewObjectId(ObjectId.zeroId());
123+
refDB.newUpdate(Constants.HEAD, false).link(Constants.R_HEADS + "main");
124+
refDB.newUpdate(Constants.R_HEADS + Constants.MASTER, false).delete();
125+
return git;
126+
}
127+
128+
private void addAndCommit(Git git, String message) throws GitAPIException {
129+
git.add().addFilepattern(".").call();
130+
git.commit().setMessage(message).call();
131+
}
132+
}

plugin-gradle/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1010
* `prettier` will now autodetect the parser (and formatter) to use based on the filename, unless you override this using `config()` or `configFile()` with the option `parser` or `filepath`. ([#620](https://github.com/diffplug/spotless/pull/620))
1111
### Fixed
1212
* LineEndings.GIT_ATTRIBUTES is now a bit more efficient, and paves the way for remote build cache support in Gradle. ([#621](https://github.com/diffplug/spotless/pull/621))
13+
* `ratchetFrom` now ratchets from the merge base of `HEAD` and the specified branch. This fixes the surprising behavior when a remote branch advanced ([#631](https://github.com/diffplug/spotless/pull/631) fixes [#627](https://github.com/diffplug/spotless/issues/627)).
1314

1415
## [4.4.0] - 2020-06-19
1516
### Added

0 commit comments

Comments
 (0)