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 f4a78036e..097b80a26 100644 --- a/jcheck/src/test/java/org/openjdk/skara/jcheck/TestRepository.java +++ b/jcheck/src/test/java/org/openjdk/skara/jcheck/TestRepository.java @@ -321,4 +321,12 @@ public String rangeInclusive(Hash from, Hash to) { public String rangeExclusive(Hash from, Hash to) { return null; } + + public List follow(Path path) { + return List.of(); + } + + public List follow(Path path, Hash from, Hash to) { + 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 f11374744..d83d566c2 100644 --- a/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java +++ b/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java @@ -120,6 +120,8 @@ default Diff diff(Hash head, List files) throws IOException { return diff(head, files, DEFAULT_SIMILARITY); } + List follow(Path path) throws IOException; + List follow(Path path, Hash base, Hash head) throws IOException; Diff diff(Hash head, List files, int similarity) throws IOException; List config(String key) throws IOException; Repository copyTo(Path destination) 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 a09835c95..bcc47075f 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 @@ -218,22 +218,7 @@ public List commitMetadata(String range, List paths, boole args.add(path.toString()); } } - var p = start(args); - var reader = new UnixStreamReader(p.getInputStream()); - var result = new ArrayList(); - - var line = reader.readLine(); - while (line != null) { - if (!line.startsWith("commit")) { - throw new IOException("Unexpected line: " + line); - } - - result.add(GitCommitMetadata.read(reader)); - line = reader.readLine(); - } - - await(p); - return result; + return readMetadata(args, "commit "); } @Override @@ -291,6 +276,49 @@ public List commitMetadata() throws IOException { return commitMetadata("--all"); } + private List readMetadata(List cmd, String delimiter) throws IOException { + var p = start(cmd); + var reader = new UnixStreamReader(p.getInputStream()); + var result = new ArrayList(); + + var line = reader.readLine(); + while (line != null) { + if (!line.startsWith(delimiter)) { + throw new IOException("Unexpected line: " + line); + } + + result.add(GitCommitMetadata.read(reader)); + line = reader.readLine(); + } + + await(p); + return result; + } + + @Override + public List follow(Path path) throws IOException { + return follow(path, null, null); + } + + @Override + public List follow(Path path, Hash from, Hash to) throws IOException { + var delimiter = "#@!_-=&"; + var cmd = new ArrayList(); + cmd.addAll(List.of("git", "log", + "--follow", + "--format=" + delimiter + "\n" + GitCommitMetadata.FORMAT, + "--topo-order", + "--no-abbrev", + "--no-color")); + if (from != null && to != null) { + cmd.add(from.hex() + ".." + to.hex()); + } + cmd.add("--"); + cmd.add(path.toString()); + return readMetadata(cmd, delimiter); + } + + private List refs() throws IOException { try (var p = capture("git", "show-ref", "--hash", "--abbrev")) { var res = p.await(); 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 fbdb3508b..4ecd7824c 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 @@ -282,18 +282,7 @@ public List commitMetadata(String range, List paths, boole if (paths != null && !paths.isEmpty()) { cmd.addAll(paths.stream().map(Path::toString).collect(Collectors.toList())); } - var p = start(cmd); - var reader = new UnixStreamReader(p.getInputStream()); - var result = new ArrayList(); - - var line = reader.readLine(); - while (line != null) { - result.add(HgCommitMetadata.read(reader)); - line = reader.readLine(); - } - - await(p); - return result; + return readMetadata(cmd); } @Override @@ -336,6 +325,38 @@ public List commitMetadata() throws IOException { return commitMetadata(null, List.of(), false); } + @Override + public List follow(Path path) throws IOException { + return follow(path, null, null); + } + + private List readMetadata(List cmd) throws IOException { + var p = start(cmd); + var reader = new UnixStreamReader(p.getInputStream()); + var result = new ArrayList(); + + var line = reader.readLine(); + while (line != null) { + result.add(HgCommitMetadata.read(reader)); + line = reader.readLine(); + } + + await(p); + return result; + } + + @Override + public List follow(Path path, Hash from, Hash to) throws IOException { + var cmd = new ArrayList(); + cmd.addAll(List.of("hg", "log", "--follow", "--template", HgCommitMetadata.TEMPLATE)); + if (from != null && to != null) { + cmd.add("--rev"); + cmd.add(from.hex() + ".." + to.hex() + " - " + from.hex()); + } + cmd.add(path.toString()); + return readMetadata(cmd); + } + @Override public boolean isEmpty() throws IOException { var numBranches = branches().size(); 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 4a094acbc..443292b58 100644 --- a/vcs/src/test/java/org/openjdk/skara/vcs/RepositoryTests.java +++ b/vcs/src/test/java/org/openjdk/skara/vcs/RepositoryTests.java @@ -2615,4 +2615,30 @@ void testTimestampOnTags(VCS vcs) throws IOException { assertEquals(date, annotated.get().date()); } } + + @ParameterizedTest + @EnumSource(VCS.class) + void testFollow(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, readme!")); + + r.add(readme); + var first = r.commit("Add README", "duke", "duke@openjdk.java.net"); + + var readme2 = dir.path().resolve("README2"); + r.move(readme, readme2); + var second = r.commit("Move README to README2", "duke", "duke@openjdk.java.net"); + + Files.write(readme2, List.of("Hello, readme2!")); + r.add(readme2); + var third = r.commit("Update README2", "duke", "duke@openjdk.java.net"); + + var commits = r.follow(readme2); + var hashes = commits.stream().map(CommitMetadata::hash).collect(Collectors.toList()); + assertEquals(List.of(third, second, first), hashes); + } + } }