diff --git a/cli/src/main/java/org/openjdk/skara/cli/GitPr.java b/cli/src/main/java/org/openjdk/skara/cli/GitPr.java index f43dc7139..b668f0b89 100644 --- a/cli/src/main/java/org/openjdk/skara/cli/GitPr.java +++ b/cli/src/main/java/org/openjdk/skara/cli/GitPr.java @@ -233,9 +233,9 @@ private static List issuesFromPullRequest(PullRequest pr) { return issues; } - private static String jbsProjectFromJcheckConf(Repository repo) throws IOException { - var conf = JCheckConfiguration.from(repo, repo.resolve("master").orElseThrow(() -> - new IOException("Could not resolve 'master' branch") + private static String jbsProjectFromJcheckConf(Repository repo, String targetBranch) throws IOException { + var conf = JCheckConfiguration.from(repo, repo.resolve(targetBranch).orElseThrow(() -> + new IOException("Could not resolve '" + targetBranch + "' branch") )); return conf.general().jbs(); @@ -898,9 +898,57 @@ public static void main(String[] args) throws IOException, InterruptedException } } + var remoteRepo = host.repository(projectName(uri)).orElseThrow(() -> + new IOException("Could not find repository at " + uri.toString()) + ); + if (token == null) { + GitCredentials.approve(credentials); + } + var parentRepo = remoteRepo.parent().orElseThrow(() -> + new IOException("error: remote repository " + remotePullPath + " is not a fork of any repository") + ); + var targetBranch = getOption("branch", "create", arguments); if (targetBranch == null) { - targetBranch = "master"; + var upstreamBranchNames = repo.remoteBranches(parentRepo.webUrl().toString()) + .stream() + .map(r -> r.name()) + .collect(Collectors.toSet()); + var remoteBranches = repo.branches(remote); + var candidates = new ArrayList(); + for (var b : remoteBranches) { + var withoutRemotePrefix = b.name().substring(remote.length() + 1); + if (upstreamBranchNames.contains(withoutRemotePrefix)) { + candidates.add(b); + } + } + + var localBranches = repo.branches(); + Branch closest = null; + var shortestDistance = Integer.MAX_VALUE; + for (var b : candidates) { + var from = b.name(); + for (var localBranch : localBranches) { + var trackingBranch = repo.upstreamFor(localBranch); + if (trackingBranch.isPresent() && + trackingBranch.get().equals(b.name())) { + from = localBranch.name(); + } + } + var distance = repo.commitMetadata(from + "..." + currentBranch.name()).size(); + if (distance < shortestDistance) { + closest = b; + shortestDistance = distance; + } + } + + if (closest != null) { + targetBranch = closest.name().substring(remote.length() + 1); + } else { + System.err.println("error: cannot automatically infer target branch"); + System.err.println(" use --branch to specify target branch"); + System.exit(1); + } } var commits = repo.commits(targetBranch + ".." + upstream.get()).asList(); if (commits.isEmpty()) { @@ -918,16 +966,7 @@ public static void main(String[] args) throws IOException, InterruptedException } } - var remoteRepo = host.repository(projectName(uri)).orElseThrow(() -> - new IOException("Could not find repository at " + uri.toString()) - ); - if (token == null) { - GitCredentials.approve(credentials); - } - var parentRepo = remoteRepo.parent().orElseThrow(() -> - new IOException("error: remote repository " + remotePullPath + " is not a fork of any repository")); - - var project = jbsProjectFromJcheckConf(repo); + var project = jbsProjectFromJcheckConf(repo, targetBranch); var issue = getIssue(currentBranch, project); var file = Files.createTempFile("PULL_REQUEST_", ".md"); if (issue.isPresent()) { 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 c67276f0a..e524abaac 100644 --- a/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java +++ b/vcs/src/main/java/org/openjdk/skara/vcs/ReadOnlyRepository.java @@ -34,6 +34,7 @@ public interface ReadOnlyRepository { Optional currentBookmark() throws IOException; Branch defaultBranch() throws IOException; List branches() throws IOException; + List branches(String remote) throws IOException; Optional defaultTag() throws IOException; List tags() throws IOException; Commits commits() 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 4d8df0d2e..ca354dbbd 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 @@ -120,6 +120,15 @@ public List branches() throws IOException { } } + public List branches(String remote) throws IOException { + try (var p = capture("git", "for-each-ref", "--format=%(refname:short)", "refs/remotes/" + remote + "/")) { + return await(p).stdout() + .stream() + .map(Branch::new) + .collect(Collectors.toList()); + } + } + public List tags() throws IOException { try (var p = capture("git", "for-each-ref", "--format=%(refname:short)", "refs/tags")) { return await(p).stdout() 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 e364eccc1..9972ba224 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 @@ -132,6 +132,12 @@ public List branches() throws IOException { } } + @Override + public List branches(String remote) throws IOException { + // Mercurial does not have namespacing of branch names + return branches(); + } + @Override public List tags() throws IOException { try (var p = capture("hg", "tags")) {