Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base ratchetFrom on the merge base #631

Merged
merged 4 commits into from
Jul 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* `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))
### Added
* `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))
* `GitRatchet` now lives in `lib-extra`, and is shared across `plugin-gradle` and `plugin-maven` ([#626](https://github.com/diffplug/spotless/pull/626)).
### Changed
* **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))
* 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)).
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ VER_SLF4J=[1.6,2.0[

# Used in multiple places
VER_DURIAN=1.2.0
VER_JGIT=5.7.0.202003110725-r
VER_JGIT=5.8.0.202006091008-r
VER_JUNIT=4.13
VER_ASSERTJ=3.15.0
VER_MOCKITO=3.3.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Nullable;

Expand All @@ -31,6 +32,7 @@
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator;
Expand Down Expand Up @@ -195,13 +197,21 @@ public synchronized ObjectId rootTreeShaOf(Project project, String reference) {
Repository repo = repositoryFor(project);
ObjectId treeSha = rootTreeShaCache.get(repo, reference);
if (treeSha == null) {
ObjectId commitSha = repo.resolve(reference);
if (commitSha == null) {
throw new IllegalArgumentException("No such reference '" + reference + "'");
}
try (RevWalk revWalk = new RevWalk(repo)) {
RevCommit revCommit = revWalk.parseCommit(commitSha);
treeSha = revCommit.getTree();
ObjectId commitSha = repo.resolve(reference);
if (commitSha == null) {
throw new IllegalArgumentException("No such reference '" + reference + "'");
}

RevCommit ratchetFrom = revWalk.parseCommit(commitSha);
RevCommit head = revWalk.parseCommit(repo.resolve(Constants.HEAD));

revWalk.setRevFilter(RevFilter.MERGE_BASE);
revWalk.markStart(ratchetFrom);
revWalk.markStart(head);

RevCommit mergeBase = revWalk.next();
treeSha = Optional.ofNullable(mergeBase).orElse(ratchetFrom).getTree();
}
rootTreeShaCache.put(repo, reference, treeSha);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright 2020 DiffPlug
*
* 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 com.diffplug.spotless.extra;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefDatabase;
import org.junit.Test;

import com.diffplug.spotless.ResourceHarness;

public class GitRachetMergeBaseTest extends ResourceHarness {
@Test
public void test() throws IllegalStateException, GitAPIException, IOException {
try (Git git = initRepo()) {
setFile("mine.txt").toContent("init");
setFile("untouched.txt").toContent("init");
addAndCommit(git, "init");

git.checkout().setCreateBranch(true).setName("rem-branch").call();
// so at the point where we start work, we are clean relative to the remote
ratchetFrom("main", "rem-branch").allClean();

// but when the remote changes
setFile("untouched.txt").toContent("changed");
addAndCommit(git, "remote");

// it shouldn't affect files that we haven't changed
git.checkout().setName("main").call();
ratchetFrom("main", "rem-branch").allClean();

// and when we work, it should continue to only affect our own work
setFile("mine.txt").toContent("changed");
ratchetFrom("main", "rem-branch").onlyDirty("mine.txt");
}
}

static class GitRatchetSimple extends GitRatchet<File> {
@Override
protected File getDir(File project) {
return project;
}

@Override
protected File getParent(File project) {
return project.getParentFile();
}
}

Asserter ratchetFrom(String... ratchetFroms) {
return new Asserter(ratchetFroms);
}

class Asserter {
final GitRatchetSimple ratchet = new GitRatchetSimple();
final String[] ratchetFroms;
final ObjectId[] shas;

Asserter(String... ratchetFrom) {
this.ratchetFroms = ratchetFrom;
this.shas = Arrays.stream(ratchetFrom)
.map(from -> ratchet.rootTreeShaOf(rootFolder(), from))
.toArray(ObjectId[]::new);
}

private void assertClean(int i, String filename, boolean expected) throws IOException {
boolean actual = ratchet.isClean(rootFolder(), shas[i], newFile(filename));
if (actual != expected) {
throw new AssertionError("Expected " + filename + " to be " + (expected ? "clean" : "dirty") + " relative to " + ratchetFroms[i]);
}
}

public void allClean() throws IOException {
onlyDirty();
}

public void allDirty() throws IOException {
String[] filenames = Arrays.stream(rootFolder().listFiles())
.filter(File::isFile)
.map(File::getName)
.toArray(String[]::new);
onlyDirty(filenames);
}

public void onlyDirty(String... filenames) throws IOException {
List<String> dirtyFiles = Arrays.asList(filenames);
for (File file : rootFolder().listFiles()) {
if (!file.isFile()) {
continue;
}
boolean expectedClean = !dirtyFiles.contains(file.getName());
for (int i = 0; i < shas.length; ++i) {
assertClean(i, file.getName(), expectedClean);
}
}
}
}

private Git initRepo() throws IllegalStateException, GitAPIException, IOException {
Git git = Git.init().setDirectory(rootFolder()).call();
RefDatabase refDB = git.getRepository().getRefDatabase();
refDB.newUpdate(Constants.R_HEADS + "main", false).setNewObjectId(ObjectId.zeroId());
refDB.newUpdate(Constants.HEAD, false).link(Constants.R_HEADS + "main");
refDB.newUpdate(Constants.R_HEADS + Constants.MASTER, false).delete();
return git;
}

private void addAndCommit(Git git, String message) throws GitAPIException {
git.add().addFilepattern(".").call();
git.commit().setMessage(message).call();
}
}
1 change: 1 addition & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* `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))
### Fixed
* 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))
* `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)).

## [4.4.0] - 2020-06-19
### Added
Expand Down