diff --git a/jcheck/src/test/java/org/openjdk/skara/jcheck/TestRepository.java b/jcheck/src/test/java/org/openjdk/skara/jcheck/TestRepository.java index f0ef052f2..3ae0d503b 100644 --- a/jcheck/src/test/java/org/openjdk/skara/jcheck/TestRepository.java +++ b/jcheck/src/test/java/org/openjdk/skara/jcheck/TestRepository.java @@ -137,6 +137,46 @@ public Optional lookup(Tag t) throws IOException { return Optional.empty(); } + public List commitMetadata(Hash from, Hash to) throws IOException { + return List.of(); + } + + public List commitMetadata(String range, boolean reverse) throws IOException { + return List.of(); + } + + public List commitMetadata(Hash from, Hash to, boolean reverse) throws IOException { + return List.of(); + } + + public List commitMetadata(List paths) throws IOException { + return List.of(); + } + + public List commitMetadata(List paths, boolean reverse) throws IOException { + return List.of(); + } + + public List commitMetadata(String range, List paths) throws IOException { + return List.of(); + } + + public List commitMetadata(Hash from, Hash to, List paths) throws IOException { + return List.of(); + } + + public List commitMetadata(String range, List paths, boolean reverse) throws IOException { + return List.of(); + } + + public List commitMetadata(Hash from, Hash to, List paths, boolean reverse) throws IOException { + return List.of(); + } + + public List commitMetadata(boolean reverse) throws IOException { + return List.of(); + } + public List commitMetadata(String range) throws IOException { return List.of(); } diff --git a/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java b/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java index b63a460df..79708bc33 100644 --- a/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java +++ b/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java @@ -49,7 +49,17 @@ public interface ReadOnlyRepository { Optional lookup(Branch b) throws IOException; Optional lookup(Tag t) throws IOException; List commitMetadata() throws IOException; + List commitMetadata(boolean reverse) throws IOException; List commitMetadata(String range) throws IOException; + List commitMetadata(Hash from, Hash to) throws IOException; + List commitMetadata(String range, boolean reverse) throws IOException; + List commitMetadata(Hash from, Hash to, boolean reverse) throws IOException; + List commitMetadata(List paths) throws IOException; + List commitMetadata(List paths, boolean reverse) throws IOException; + List commitMetadata(String range, List paths) throws IOException; + List commitMetadata(Hash from, Hash to, List paths) throws IOException; + List commitMetadata(String range, List paths, boolean reverse) throws IOException; + List commitMetadata(Hash from, Hash to, List paths, boolean reverse) throws IOException; Path root() throws IOException; boolean exists() throws IOException; boolean isHealthy() throws IOException; diff --git a/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java b/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java index dad1037f5..697d2203a 100644 --- a/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java +++ b/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java @@ -200,8 +200,23 @@ public Optional lookup(Tag t) throws IOException { } @Override - public List commitMetadata(String range) throws IOException { - var p = start("git", "rev-list", "--format=" + GitCommitMetadata.FORMAT, "--no-abbrev", "--reverse", "--no-color", range); + public List commitMetadata(String range, List paths, boolean reverse) throws IOException { + var args = new ArrayList(); + args.addAll(List.of("git", "rev-list", + "--format=" + GitCommitMetadata.FORMAT, + "--no-abbrev", + "--no-color", + range)); + if (reverse) { + args.add("--reverse"); + } + if (paths != null && !paths.isEmpty()) { + args.add("--"); + for (var path : paths) { + args.add(path.toString()); + } + } + var p = start(args); var reader = new UnixStreamReader(p.getInputStream()); var result = new ArrayList(); @@ -219,6 +234,56 @@ public List commitMetadata(String range) throws IOException { return result; } + @Override + public List commitMetadata(Hash from, Hash to, List paths, boolean reverse) throws IOException { + return commitMetadata(from.hex() + ".." + to.hex(), paths, reverse); + } + + @Override + public List commitMetadata(String range, List paths) throws IOException { + return commitMetadata(range, paths, false); + } + + @Override + public List commitMetadata(Hash from, Hash to, List paths) throws IOException { + return commitMetadata(from.hex() + ".." + to.hex(), paths, false); + } + + @Override + public List commitMetadata(boolean reverse) throws IOException { + return commitMetadata("--all", List.of(), reverse); + } + + @Override + public List commitMetadata(String range) throws IOException { + return commitMetadata(range, List.of(), false); + } + + @Override + public List commitMetadata(Hash from, Hash to) throws IOException { + return commitMetadata(from.hex() + ".." + to.hex(), List.of(), false); + } + + @Override + public List commitMetadata(String range, boolean reverse) throws IOException { + return commitMetadata(range, List.of(), reverse); + } + + @Override + public List commitMetadata(Hash from, Hash to, boolean reverse) throws IOException { + return commitMetadata(from.hex() + ".." + to.hex(), List.of(), reverse); + } + + @Override + public List commitMetadata(List paths) throws IOException { + return commitMetadata("--all", paths, false); + } + + @Override + public List commitMetadata(List paths, boolean reverse) throws IOException { + return commitMetadata("--all", paths, reverse); + } + @Override public List commitMetadata() throws IOException { return commitMetadata("--all"); diff --git a/vcs/src/main/java/org/openjdk/skara/vcs/hg/HgRepository.java b/vcs/src/main/java/org/openjdk/skara/vcs/hg/HgRepository.java index b9a42445c..15ea12612 100644 --- a/vcs/src/main/java/org/openjdk/skara/vcs/hg/HgRepository.java +++ b/vcs/src/main/java/org/openjdk/skara/vcs/hg/HgRepository.java @@ -257,16 +257,34 @@ public Optional lookup(Tag t) throws IOException { } @Override - public List commitMetadata(String range) throws IOException { - throw new RuntimeException("not implemented yet"); + public List commitMetadata(String range, List paths) throws IOException { + return commitMetadata(range, paths, false); } @Override - public List commitMetadata() throws IOException { + public List commitMetadata(Hash from, Hash to, List paths) throws IOException { + return commitMetadata(from.hex() + ":" + to.hex() + "-" + from.hex(), paths, false); + } + + @Override + public List commitMetadata(Hash from, Hash to, List paths, boolean reverse) throws IOException { + return commitMetadata(from.hex() + ":" + to.hex() + "-" + from.hex(), paths, reverse); + } + + @Override + public List commitMetadata(String range, List paths, boolean reverse) throws IOException { var ext = Files.createTempFile("ext", ".py"); copyResource(EXT_PY, ext); - var p = start("hg", "--config", "extensions.dump=" + ext.toAbsolutePath().toString(), "metadata"); + var args = new ArrayList(); + args.addAll(List.of("hg", "--config", "extensions.dump=" + ext.toAbsolutePath().toString(), "metadata")); + range = range == null ? "tip:0" : range; + var revset = reverse ? "reverse(" + range + ")" : range; + args.add(revset); + if (paths != null && !paths.isEmpty()) { + args.add(paths.stream().map(Path::toString).collect(Collectors.joining("\t"))); + } + var p = start(args); var reader = new UnixStreamReader(p.getInputStream()); var result = new ArrayList(); @@ -280,6 +298,46 @@ public List commitMetadata() throws IOException { return result; } + @Override + public List commitMetadata(String range) throws IOException { + return commitMetadata(range, List.of(), false); + } + + @Override + public List commitMetadata(boolean reverse) throws IOException { + return commitMetadata(null, List.of(), reverse); + } + + @Override + public List commitMetadata(Hash from, Hash to) throws IOException { + return commitMetadata(from.hex() + ":" + to.hex() + "-" + from.hex(), List.of(), false); + } + + @Override + public List commitMetadata(String range, boolean reverse) throws IOException { + return commitMetadata(range, List.of(), reverse); + } + + @Override + public List commitMetadata(Hash from, Hash to, boolean reverse) throws IOException { + return commitMetadata(from.hex() + ":" + to.hex() + "-" + from.hex(), reverse); + } + + @Override + public List commitMetadata(List paths) throws IOException { + return commitMetadata(null, paths, false); + } + + @Override + public List commitMetadata(List paths, boolean reverse) throws IOException { + return commitMetadata(null, paths, reverse); + } + + @Override + public List commitMetadata() throws IOException { + return commitMetadata(null, List.of(), false); + } + @Override public boolean isEmpty() throws IOException { var numBranches = branches().size(); diff --git a/vcs/src/main/resources/ext.py b/vcs/src/main/resources/ext.py index 7f4b1cd19..1f620ae6e 100644 --- a/vcs/src/main/resources/ext.py +++ b/vcs/src/main/resources/ext.py @@ -304,13 +304,18 @@ def dump(ui, repo, **opts): __dump(repo, 0, len(repo)) @command(b'metadata', [], b'hg metadata') -def dump(ui, repo, revs=None, **opts): - if revs == None: - revs = b"0:tip" +def metadata(ui, repo, revs, filenames=None, **opts): + if filenames != None: + fnames = filenames.split(b"\t") for r in revrange(repo, [revs]): ctx = repo[r] - __dump_metadata(ctx) + if filenames == None: + __dump_metadata(ctx) + else: + modified, added, removed = tuple(ctx.status(ctx.p1(), _match_exact(repo.root, repo.getcwd(), fnames)))[:3] + if modified or added or removed: + __dump_metadata(ctx) @command(b'ls-tree', [], b'hg ls-tree') def ls_tree(ui, repo, rev, **opts): diff --git a/vcs/src/test/java/org/openjdk/skara/vcs/RepositoryTests.java b/vcs/src/test/java/org/openjdk/skara/vcs/RepositoryTests.java index de1d000b8..fc15e1f32 100644 --- a/vcs/src/test/java/org/openjdk/skara/vcs/RepositoryTests.java +++ b/vcs/src/test/java/org/openjdk/skara/vcs/RepositoryTests.java @@ -1197,11 +1197,75 @@ void testCommitMetadata(VCS vcs) throws IOException { var metadata = r.commitMetadata(); assertEquals(2, metadata.size()); + assertEquals(second, metadata.get(0).hash()); + assertEquals(List.of("Modified README"), metadata.get(0).message()); + + assertEquals(first, metadata.get(1).hash()); + assertEquals(List.of("Added README"), metadata.get(1).message()); + } + } + + @ParameterizedTest + @EnumSource(VCS.class) + void testCommitMetadataWithFiles(VCS vcs) throws IOException { + try (var dir = new TemporaryDirectory()) { + var r = Repository.init(dir.path(), vcs); + + var readme1 = dir.path().resolve("README_1"); + Files.write(readme1, List.of("1")); + r.add(readme1); + var first = r.commit("Added README_1", "duke", "duke@openjdk.java.net"); + + var readme2 = dir.path().resolve("README_2"); + Files.write(readme2, List.of("2")); + r.add(readme2); + var second = r.commit("Added README_2", "duke", "duke@openjdk.java.net"); + + Files.write(readme2, List.of("3"), WRITE, APPEND); + r.add(readme2); + var third = r.commit("Modified README_2", "duke", "duke@openjdk.java.net"); + + var metadata = r.commitMetadata(List.of(Path.of("README_1"))); + assertEquals(1, metadata.size()); assertEquals(first, metadata.get(0).hash()); - assertEquals(List.of("Added README"), metadata.get(0).message()); + metadata = r.commitMetadata(List.of(Path.of("README_2"))); + assertEquals(2, metadata.size()); + assertEquals(third, metadata.get(0).hash()); + assertEquals(second, metadata.get(1).hash()); + + metadata = r.commitMetadata(List.of(Path.of("README_1"), Path.of("README_2"))); + assertEquals(3, metadata.size()); + assertEquals(third, metadata.get(0).hash()); + assertEquals(second, metadata.get(1).hash()); + assertEquals(first, metadata.get(2).hash()); + } + } + + @ParameterizedTest + @EnumSource(VCS.class) + void testCommitMetadataWithReverse(VCS vcs) throws IOException { + try (var dir = new TemporaryDirectory()) { + var r = Repository.init(dir.path(), vcs); + + var readme = dir.path().resolve("README"); + Files.write(readme, List.of("Hello, world!")); + r.add(readme); + var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); + + Files.write(readme, List.of("One more line"), WRITE, APPEND); + r.add(readme); + var second = r.commit("Modified README", "duke", "duke@openjdk.java.net"); + + var metadata = r.commitMetadata(); + assertEquals(2, metadata.size()); + assertEquals(second, metadata.get(0).hash()); + assertEquals(first, metadata.get(1).hash()); + + metadata = r.commitMetadata(true); + assertEquals(2, metadata.size()); + assertEquals(first, metadata.get(0).hash()); assertEquals(second, metadata.get(1).hash()); - assertEquals(List.of("Modified README"), metadata.get(1).message()); } } diff --git a/webrev/src/main/java/org/openjdk/skara/webrev/AddedFileView.java b/webrev/src/main/java/org/openjdk/skara/webrev/AddedFileView.java index 716bde416..d201401d6 100644 --- a/webrev/src/main/java/org/openjdk/skara/webrev/AddedFileView.java +++ b/webrev/src/main/java/org/openjdk/skara/webrev/AddedFileView.java @@ -27,17 +27,22 @@ import java.io.*; import java.nio.file.*; import java.util.List; +import java.util.stream.Collectors; class AddedFileView implements FileView { private final Patch patch; private final Path out; + private final List commits; + private final MetadataFormatter formatter; private final List newContent; private final byte[] binaryContent; private final WebrevStats stats; - public AddedFileView(ReadOnlyRepository repo, Hash head, Patch patch, Path out) throws IOException { + public AddedFileView(ReadOnlyRepository repo, Hash base, Hash head, List commits, MetadataFormatter formatter, Patch patch, Path out) throws IOException { this.patch = patch; this.out = out; + this.commits = commits; + this.formatter = formatter; if (patch.isTextual()) { binaryContent = null; if (head == null) { @@ -104,6 +109,11 @@ public void render(Writer w) throws IOException { if (patch.isTextual()) { w.write("
\n"); + w.write("
\n");
+            w.write(commits.stream()
+                           .map(formatter::format)
+                           .collect(Collectors.joining("\n")));
+            w.write("  
\n"); w.write(" \n"); w.write(stats.toString()); w.write(" "); diff --git a/webrev/src/main/java/org/openjdk/skara/webrev/MetadataFormatter.java b/webrev/src/main/java/org/openjdk/skara/webrev/MetadataFormatter.java new file mode 100644 index 000000000..b49275a66 --- /dev/null +++ b/webrev/src/main/java/org/openjdk/skara/webrev/MetadataFormatter.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.skara.webrev; + +import org.openjdk.skara.vcs.*; + +class MetadataFormatter { + String format(CommitMetadata metadata) { + return "" + metadata.hash().abbreviate() +": " + metadata.message().get(0); + } +} diff --git a/webrev/src/main/java/org/openjdk/skara/webrev/ModifiedFileView.java b/webrev/src/main/java/org/openjdk/skara/webrev/ModifiedFileView.java index 42fdb48ac..4bca25607 100644 --- a/webrev/src/main/java/org/openjdk/skara/webrev/ModifiedFileView.java +++ b/webrev/src/main/java/org/openjdk/skara/webrev/ModifiedFileView.java @@ -27,20 +27,25 @@ import java.io.*; import java.nio.file.*; import java.util.List; +import java.util.stream.Collectors; class ModifiedFileView implements FileView { private final Patch patch; private final Path out; private final Navigation navigation; + private final List commits; + private final MetadataFormatter formatter; private final List oldContent; private final List newContent; private final byte[] binaryContent; private final WebrevStats stats; - public ModifiedFileView(ReadOnlyRepository repo, Hash base, Hash head, Patch patch, Path out, Navigation navigation) throws IOException { + public ModifiedFileView(ReadOnlyRepository repo, Hash base, Hash head, List commits, MetadataFormatter formatter, Patch patch, Path out, Navigation navigation) throws IOException { this.patch = patch; this.out = out; this.navigation = navigation; + this.commits = commits; + this.formatter = formatter; if (patch.isTextual()) { binaryContent = null; oldContent = repo.lines(patch.source().path().get(), base).orElseThrow(() -> { @@ -168,6 +173,11 @@ public void render(Writer w) throws IOException { if (patch.isTextual()) { w.write("
\n"); + w.write("
\n");
+            w.write(commits.stream()
+                           .map(formatter::format)
+                           .collect(Collectors.joining("\n")));
+            w.write("  
\n"); w.write(" \n"); w.write(stats.toString()); w.write(" "); diff --git a/webrev/src/main/java/org/openjdk/skara/webrev/RemovedFileView.java b/webrev/src/main/java/org/openjdk/skara/webrev/RemovedFileView.java index 052e9e1b9..4d6d5bcca 100644 --- a/webrev/src/main/java/org/openjdk/skara/webrev/RemovedFileView.java +++ b/webrev/src/main/java/org/openjdk/skara/webrev/RemovedFileView.java @@ -27,17 +27,22 @@ import java.io.*; import java.nio.file.Path; import java.util.List; +import java.util.stream.Collectors; class RemovedFileView implements FileView { private final Patch patch; private final Path out; + private final List commits; + private final MetadataFormatter formatter; private final List oldContent; private final byte[] binaryContent; private final WebrevStats stats; - public RemovedFileView(ReadOnlyRepository repo, Hash base, Patch patch, Path out) throws IOException { + public RemovedFileView(ReadOnlyRepository repo, Hash base, Hash head, List commits, MetadataFormatter formatter, Patch patch, Path out) throws IOException { this.patch = patch; this.out = out; + this.commits = commits; + this.formatter = formatter; if (patch.isTextual()) { binaryContent = null; oldContent = repo.lines(patch.source().path().get(), base).orElseThrow(IllegalArgumentException::new); @@ -97,6 +102,11 @@ public void render(Writer w) throws IOException { if (patch.isTextual()) { w.write("
\n"); + w.write("
\n");
+            w.write(commits.stream()
+                           .map(formatter::format)
+                           .collect(Collectors.joining("\n")));
+            w.write("  
\n"); w.write(" \n"); w.write(stats.toString()); w.write(" "); diff --git a/webrev/src/main/java/org/openjdk/skara/webrev/Webrev.java b/webrev/src/main/java/org/openjdk/skara/webrev/Webrev.java index 90921fa50..195e9ea87 100644 --- a/webrev/src/main/java/org/openjdk/skara/webrev/Webrev.java +++ b/webrev/src/main/java/org/openjdk/skara/webrev/Webrev.java @@ -172,16 +172,22 @@ public void generate(Hash tailEnd, Hash head) throws IOException { navigations.addLast(new Navigation(prev, next)); } + var headHash = head == null ? repository.head() : head; var fileViews = new ArrayList(); + var formatter = new MetadataFormatter(); for (var patch : patches) { var status = patch.status(); + var path = status.isDeleted() ? + patch.source().path().get() : + patch.target().path().get(); + var commits = repository.commitMetadata(tailEnd, headHash, List.of(path)); if (status.isModified() || status.isRenamed() || status.isCopied()) { var nav = navigations.removeFirst(); - fileViews.add(new ModifiedFileView(repository, tailEnd, head, patch, output, nav)); + fileViews.add(new ModifiedFileView(repository, tailEnd, head, commits, formatter, patch, output, nav)); } else if (status.isAdded()) { - fileViews.add(new AddedFileView(repository, head, patch, output)); + fileViews.add(new AddedFileView(repository, tailEnd, head, commits, formatter, patch, output)); } else if (status.isDeleted()) { - fileViews.add(new RemovedFileView(repository, tailEnd, patch, output)); + fileViews.add(new RemovedFileView(repository, tailEnd, head, commits, formatter, patch, output)); } }