Merge "Support branch specific labels"
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 7671767..74f56b3 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -37,6 +37,13 @@
which buck
----
+If you plan to use the link:#buck-daemon[Buck daemon] add a symbolic
+link in `~/bin` to the buckd executable:
+
+----
+ ln -s `pwd`/bin/buckd ~/bin/
+----
+
[[eclipse]]
Eclipse Integration
@@ -289,7 +296,6 @@
)
----
-
Caching Build Results
~~~~~~~~~~~~~~~~~~~~~
@@ -306,6 +312,29 @@
EOF
----
+[[buck-daemon]]
+Using Buck daemon
+~~~~~~~~~~~~~~~~~
+
+Buck ships with daemon command `buckd`, which uses
+link:https://github.com/martylamb/nailgun[Nailgun] protocol for running
+Java programs from the command line without incurring the JVM startup
+overhead. Using a Buck daemon can save significant amounts of time as it
+avoids the overhead of starting a Java virtual machine, loading the buck class
+files and parsing the build files for each command. It is safe to run several
+buck daemons started from different project directories and they will not
+interfere with each other. Buck's documentation covers daemon in
+http://facebook.github.io/buck/command/buckd.html[buckd]. The trivial case is
+to run `buckd` from the project's root directory and use `buck` as usually:
+
+----
+$>buckd
+$>buck build gerrit
+Using buckd.
+[-] PARSING BUILD FILES...FINISHED 0.6s
+[-] BUILDING...FINISHED 0.2s
+----
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index d7e9fd7..f1c13f8 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -168,6 +168,11 @@
+
Changes that match 'MESSAGE' arbitrary string in the commit message body.
+[[comment]]
+comment:'TEXT'::
++
+Changes that match 'TEXT' string in any comment left by a reviewer.
+
[[file]]
file:^'REGEX'::
+
@@ -187,12 +192,6 @@
ones using a bracket expression). For example, to match all XML
files named like 'name1.xml', 'name2.xml', and 'name3.xml' use
`file:"^name[1-3].xml"`.
-+
-Currently this operator is only available on a watched project
-and may not be used in the search bar. The same holds true for web UI
-"My > Watched Changes", i. e. file:regex is used over the is:watched
-expression. It never produces any results, because the error message:
-"operator not permitted here: file:regex" is suppressed.
[[has]]
has:draft::
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
index f56ef07..cb785b9 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -114,6 +114,12 @@
return create(username, null, username, (String[]) null);
}
+ public TestAccount admin()
+ throws UnsupportedEncodingException, OrmException, JSchException {
+ return create("admin", "admin@example.com", "Administrator",
+ "Administrators");
+ }
+
private AccountExternalId.Key getEmailKey(String email) {
return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email);
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index a9ce827..de70796 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -84,7 +84,8 @@
File tmp = TempFileUtil.createTempDirectory();
Init init = new Init();
int rc = init.main(new String[] {
- "-d", tmp.getPath(), "--batch", "--no-auto-start"});
+ "-d", tmp.getPath(), "--batch", "--no-auto-start",
+ "--skip-plugins"});
if (rc != 0) {
throw new RuntimeException("Couldn't initialize site");
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectDeletedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectDeletedListener.java
new file mode 100644
index 0000000..b03f99c
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectDeletedListener.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.google.gerrit.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Notified whenever a project is deleted on the master. */
+@ExtensionPoint
+public interface ProjectDeletedListener {
+ public interface Event {
+ String getProjectName();
+ }
+
+ void onProjectDeleted(Event event);
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java
index c07ee51..0c7df3e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/IncludedInDetailFactory.java
@@ -20,6 +20,7 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.change.IncludedInResolver;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -29,23 +30,15 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
/** Creates a {@link IncludedInDetail} of a {@link Change}. */
class IncludedInDetailFactory extends Handler<IncludedInDetail> {
- private static final Logger log =
- LoggerFactory.getLogger(IncludedInDetailFactory.class);
interface Factory {
IncludedInDetailFactory create(Change.Id id);
@@ -56,7 +49,6 @@
private final GitRepositoryManager repoManager;
private final Change.Id changeId;
- private IncludedInDetail detail;
private ChangeControl control;
@Inject
@@ -92,11 +84,7 @@
throw new InvalidRevisionException();
}
- detail = new IncludedInDetail();
- detail.setBranches(includedIn(repo, rw, rev, Constants.R_HEADS));
- detail.setTags(includedIn(repo, rw, rev, Constants.R_TAGS));
-
- return detail;
+ return IncludedInResolver.resolve(repo, rw, rev);
} finally {
rw.release();
}
@@ -104,32 +92,4 @@
repo.close();
}
}
-
- private List<String> includedIn(final Repository repo, final RevWalk rw,
- final RevCommit rev, final String namespace) throws IOException,
- MissingObjectException, IncorrectObjectTypeException {
- final List<String> result = new ArrayList<String>();
- for (final Ref ref : repo.getRefDatabase().getRefs(namespace).values()) {
- final RevCommit tip;
- try {
- tip = rw.parseCommit(ref.getObjectId());
- } catch (IncorrectObjectTypeException notCommit) {
- // Its OK for a tag reference to point to a blob or a tree, this
- // is common in the Linux kernel or git.git repository.
- //
- continue;
- } catch (MissingObjectException notHere) {
- // Log the problem with this branch, but keep processing.
- //
- log.warn("Reference " + ref.getName() + " in " + repo.getDirectory()
- + " points to dangling object " + ref.getObjectId());
- continue;
- }
-
- if (rw.isMergedInto(rev, tip)) {
- result.add(ref.getName().substring(namespace.length()));
- }
- }
- return result;
- }
-}
+}
\ No newline at end of file
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 286565b..c98bd5d 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -48,6 +48,7 @@
import com.google.inject.assistedinject.AssistedInject;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.analysis.util.CharArraySet;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
@@ -112,7 +113,7 @@
private static IndexWriterConfig getIndexWriterConfig(Config cfg, String name) {
IndexWriterConfig writerConfig = new IndexWriterConfig(LUCENE_VERSION,
- new StandardAnalyzer(LUCENE_VERSION));
+ new StandardAnalyzer(LUCENE_VERSION, CharArraySet.EMPTY_SET));
writerConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
double m = 1 << 20;
writerConfig.setRAMBufferSizeMB(cfg.getLong("index", name, "ramBufferSize",
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 0f0b208..ae1ab71 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -70,6 +70,9 @@
@Option(name = "--no-auto-start", usage = "Don't automatically start daemon after init")
private boolean noAutoStart;
+ @Option(name = "--skip-plugins", usage = "Don't install plugin")
+ private boolean skipPlugins = false;
+
@Option(name = "--list-plugins", usage = "List available plugins")
private boolean listPlugins;
@@ -90,22 +93,25 @@
ErrorLogFile.errorOnlyConsole();
final SiteInit init = createSiteInit();
- final List<PluginData> plugins = InitPlugins.listPlugins(init.site);
- ConsoleUI ui = ConsoleUI.getInstance(false);
- verifyInstallPluginList(ui, plugins);
- if (listPlugins) {
- if (!plugins.isEmpty()) {
- ui.message("Available plugins:\n");
- for (PluginData plugin : plugins) {
- ui.message(" * %s version %s\n", plugin.name, plugin.version);
+ if (!skipPlugins) {
+ final List<PluginData> plugins = InitPlugins.listPlugins(init.site);
+ ConsoleUI ui = ConsoleUI.getInstance(false);
+ verifyInstallPluginList(ui, plugins);
+ if (listPlugins) {
+ if (!plugins.isEmpty()) {
+ ui.message("Available plugins:\n");
+ for (PluginData plugin : plugins) {
+ ui.message(" * %s version %s\n", plugin.name, plugin.version);
+ }
+ } else {
+ ui.message("No plugins found.\n");
}
- } else {
- ui.message("No plugins found.\n");
+ return 0;
}
- return 0;
}
init.flags.autoStart = !noAutoStart && init.site.isNew;
+ init.flags.skipPlugins = skipPlugins;
final SiteRun run;
try {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
index adbb03a..267b41a 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
@@ -34,10 +34,14 @@
/** Run the daemon (and open the web UI in a browser) after initialization. */
public boolean autoStart;
+ /** Skip plugins */
+ public boolean skipPlugins;
+
public final FileBasedConfig cfg;
public final FileBasedConfig sec;
public final List<String> installPlugins;
+
@Inject
InitFlags(final SitePaths site,
final @InstallPlugins List<String> installPlugins) throws IOException,
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
index e71f3d1..1c2d024 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
@@ -75,6 +75,10 @@
mkdir(site.data_dir);
for (InitStep step : steps) {
+ if (step instanceof InitPlugins
+ && flags.skipPlugins) {
+ continue;
+ }
step.run();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
new file mode 100644
index 0000000..b02f6f0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.google.gerrit.server.change;
+
+import com.google.gerrit.common.data.IncludedInDetail;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Resolve in which tags and branches a commit is included.
+ */
+public class IncludedInResolver {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(IncludedInResolver.class);
+
+ public static IncludedInDetail resolve(final Repository repo,
+ final RevWalk rw, final RevCommit commit) throws IOException {
+
+ Set<Ref> tags =
+ new HashSet<Ref>(repo.getRefDatabase().getRefs(Constants.R_TAGS)
+ .values());
+ Set<Ref> branches =
+ new HashSet<Ref>(repo.getRefDatabase().getRefs(Constants.R_HEADS)
+ .values());
+ Set<Ref> allTagsAndBranches = new HashSet<Ref>();
+ allTagsAndBranches.addAll(tags);
+ allTagsAndBranches.addAll(branches);
+ Set<Ref> allMatchingTagsAndBranches =
+ includedIn(repo, rw, commit, allTagsAndBranches);
+
+ IncludedInDetail detail = new IncludedInDetail();
+ detail
+ .setBranches(getMatchingRefNames(allMatchingTagsAndBranches, branches));
+ detail.setTags(getMatchingRefNames(allMatchingTagsAndBranches, tags));
+
+ return detail;
+ }
+
+ /**
+ * Resolves which tip refs include the target commit.
+ */
+ private static Set<Ref> includedIn(final Repository repo, final RevWalk rw,
+ final RevCommit target, final Set<Ref> tipRefs) throws IOException,
+ MissingObjectException, IncorrectObjectTypeException {
+
+ Set<Ref> result = new HashSet<Ref>();
+
+ Map<RevCommit, Set<Ref>> tipsAndCommits = parseCommits(repo, rw, tipRefs);
+
+ List<RevCommit> tips = new ArrayList<RevCommit>(tipsAndCommits.keySet());
+ Collections.sort(tips, new Comparator<RevCommit>() {
+ @Override
+ public int compare(RevCommit c1, RevCommit c2) {
+ return c1.getCommitTime() - c2.getCommitTime();
+ }
+ });
+
+ Set<RevCommit> targetReachableFrom = new HashSet<RevCommit>();
+ targetReachableFrom.add(target);
+
+ for (RevCommit tip : tips) {
+ boolean commitFound = false;
+ rw.resetRetain(RevFlag.UNINTERESTING);
+ rw.markStart(tip);
+ for (RevCommit commit : rw) {
+ if (targetReachableFrom.contains(commit)) {
+ commitFound = true;
+ targetReachableFrom.add(tip);
+ result.addAll(tipsAndCommits.get(tip));
+ break;
+ }
+ }
+ if (!commitFound) {
+ rw.markUninteresting(tip);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the short names of refs which are as well in the matchingRefs list
+ * as well as in the allRef list.
+ */
+ private static List<String> getMatchingRefNames(Set<Ref> matchingRefs,
+ Set<Ref> allRefs) {
+ List<String> refNames = new ArrayList<String>();
+ for (Ref matchingRef : matchingRefs) {
+ if (allRefs.contains(matchingRef)) {
+ refNames.add(Repository.shortenRefName(matchingRef.getName()));
+ }
+ }
+ return refNames;
+ }
+
+ /**
+ * Parse commit of ref and store the relation between ref and commit.
+ */
+ private static Map<RevCommit, Set<Ref>> parseCommits(final Repository repo,
+ final RevWalk rw, final Set<Ref> refs) throws IOException {
+ Map<RevCommit, Set<Ref>> result = new HashMap<RevCommit, Set<Ref>>();
+ for (Ref ref : refs) {
+ final RevCommit commit;
+ try {
+ commit = rw.parseCommit(ref.getObjectId());
+ } catch (IncorrectObjectTypeException notCommit) {
+ // Its OK for a tag reference to point to a blob or a tree, this
+ // is common in the Linux kernel or git.git repository.
+ //
+ continue;
+ } catch (MissingObjectException notHere) {
+ // Log the problem with this branch, but keep processing.
+ //
+ log.warn("Reference " + ref.getName() + " in " + repo.getDirectory()
+ + " points to dangling object " + ref.getObjectId());
+ continue;
+ }
+ Set<Ref> relatedRefs = result.get(commit);
+ if (relatedRefs == null) {
+ relatedRefs = new HashSet<Ref>();
+ result.put(commit, relatedRefs);
+ }
+ relatedRefs.add(ref);
+ }
+ return result;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index 5a6d441..f41d97d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -48,6 +48,7 @@
import com.google.gerrit.server.change.ReviewerJson.ReviewerInfo;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.AddReviewerSender;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
@@ -94,6 +95,7 @@
private final ChangeHooks hooks;
private final AccountCache accountCache;
private final ReviewerJson json;
+ private final ChangeIndexer indexer;
@Inject
PostReviewers(AccountsCollection accounts,
@@ -108,7 +110,8 @@
@GerritServerConfig Config cfg,
ChangeHooks hooks,
AccountCache accountCache,
- ReviewerJson json) {
+ ReviewerJson json,
+ ChangeIndexer indexer) {
this.accounts = accounts;
this.reviewerFactory = reviewerFactory;
this.addReviewerSenderFactory = addReviewerSenderFactory;
@@ -122,6 +125,7 @@
this.hooks = hooks;
this.accountCache = accountCache;
this.json = json;
+ this.indexer = indexer;
}
@Override
@@ -255,6 +259,7 @@
db.rollback();
}
+ indexer.index(rsrc.getChange());
accountLoaderFactory.create(true).fill(result.reviewers);
postAdd(rsrc.getChange(), result);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java
index 2fbc79f..fd0596e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/PublishDraft.java
@@ -15,18 +15,11 @@
package com.google.gerrit.server.changedetail;
-import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromApprovals;
-import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
-
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.ReviewResult;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -35,7 +28,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.CreateChangeSender;
-import com.google.gerrit.server.mail.MailUtil.MailRecipients;
+import com.google.gerrit.server.mail.PatchSetNotificationSender;
import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
@@ -46,23 +39,10 @@
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.FooterLine;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
import java.util.concurrent.Callable;
public class PublishDraft implements Callable<ReviewResult> {
- private static final Logger log =
- LoggerFactory.getLogger(PublishDraft.class);
-
public interface Factory {
PublishDraft create(PatchSet.Id patchSetId);
}
@@ -70,13 +50,8 @@
private final ChangeControl.Factory changeControlFactory;
private final ReviewDb db;
private final ChangeHooks hooks;
- private final GitRepositoryManager repoManager;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final ApprovalsUtil approvalsUtil;
- private final AccountResolver accountResolver;
- private final CreateChangeSender.Factory createChangeSenderFactory;
- private final ReplacePatchSetSender.Factory replacePatchSetFactory;
private final ChangeIndexer indexer;
+ private final PatchSetNotificationSender sender;
private final PatchSet.Id patchSetId;
@@ -90,17 +65,13 @@
final CreateChangeSender.Factory createChangeSenderFactory,
final ReplacePatchSetSender.Factory replacePatchSetFactory,
final ChangeIndexer indexer,
+ final PatchSetNotificationSender sender,
@Assisted final PatchSet.Id patchSetId) {
this.changeControlFactory = changeControlFactory;
this.db = db;
this.hooks = hooks;
- this.repoManager = repoManager;
- this.patchSetInfoFactory = patchSetInfoFactory;
- this.approvalsUtil = approvalsUtil;
- this.accountResolver = accountResolver;
- this.createChangeSenderFactory = createChangeSenderFactory;
- this.replacePatchSetFactory = replacePatchSetFactory;
this.indexer = indexer;
+ this.sender = sender;
this.patchSetId = patchSetId;
}
@@ -153,7 +124,7 @@
indexer.index(updatedChange);
hooks.doDraftPublishedHook(updatedChange, updatedPatchSet, db);
- sendNotifications(control.getChange().getStatus() == Change.Status.DRAFT,
+ sender.send(control.getChange().getStatus() == Change.Status.DRAFT,
(IdentifiedUser) control.getCurrentUser(), updatedChange, updatedPatchSet,
labelTypes);
}
@@ -161,66 +132,4 @@
return result;
}
-
- private void sendNotifications(final boolean newChange,
- final IdentifiedUser currentUser, final Change updatedChange,
- final PatchSet updatedPatchSet, final LabelTypes labelTypes)
- throws OrmException, IOException, PatchSetInfoNotAvailableException {
- final Repository git = repoManager.openRepository(updatedChange.getProject());
- try {
- final RevWalk revWalk = new RevWalk(git);
- final RevCommit commit;
- try {
- commit = revWalk.parseCommit(ObjectId.fromString(updatedPatchSet.getRevision().get()));
- } finally {
- revWalk.release();
- }
- final PatchSetInfo info = patchSetInfoFactory.get(commit, updatedPatchSet.getId());
- final List<FooterLine> footerLines = commit.getFooterLines();
- final Account.Id me = currentUser.getAccountId();
- final MailRecipients recipients =
- getRecipientsFromFooters(accountResolver, updatedPatchSet, footerLines);
- recipients.remove(me);
-
- if (newChange) {
- approvalsUtil.addReviewers(db, labelTypes, updatedChange, updatedPatchSet, info,
- recipients.getReviewers(), Collections.<Account.Id> emptySet());
- try {
- CreateChangeSender cm = createChangeSenderFactory.create(updatedChange);
- cm.setFrom(me);
- cm.setPatchSet(updatedPatchSet, info);
- cm.addReviewers(recipients.getReviewers());
- cm.addExtraCC(recipients.getCcOnly());
- cm.send();
- } catch (Exception e) {
- log.error("Cannot send email for new change " + updatedChange.getId(), e);
- }
- } else {
- final List<PatchSetApproval> patchSetApprovals =
- db.patchSetApprovals().byChange(updatedChange.getId()).toList();
- final MailRecipients oldRecipients =
- getRecipientsFromApprovals(patchSetApprovals);
- approvalsUtil.addReviewers(db, labelTypes, updatedChange, updatedPatchSet, info,
- recipients.getReviewers(), oldRecipients.getAll());
- final ChangeMessage msg =
- new ChangeMessage(new ChangeMessage.Key(updatedChange.getId(),
- ChangeUtil.messageUUID(db)), me,
- updatedPatchSet.getCreatedOn(), updatedPatchSet.getId());
- msg.setMessage("Uploaded patch set " + updatedPatchSet.getPatchSetId() + ".");
- try {
- ReplacePatchSetSender cm = replacePatchSetFactory.create(updatedChange);
- cm.setFrom(me);
- cm.setPatchSet(updatedPatchSet, info);
- cm.setChangeMessage(msg);
- cm.addReviewers(recipients.getReviewers());
- cm.addExtraCC(recipients.getCcOnly());
- cm.send();
- } catch (Exception e) {
- log.error("Cannot send email for new patch set " + updatedPatchSet.getId(), e);
- }
- }
- } finally {
- git.close();
- }
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 3f7acf6..50b8075 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
+import com.google.gerrit.extensions.events.ProjectDeletedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -231,6 +232,7 @@
DynamicMap.mapOf(binder(), CapabilityDefinition.class);
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
+ DynamicSet.setOf(binder(), ProjectDeletedListener.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ChangeCache.class);
DynamicSet.setOf(binder(), ChangeListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
index dd94577..ccd6c89 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
@@ -126,7 +126,10 @@
@Override
public int getCost() {
- return pred.getCost();
+ // Index queries are assumed to be cheaper than any other type of query, so
+ // so try to make sure they get picked. Note that pred's cost may be higher
+ // because it doesn't know whether it's being used in an index query or not.
+ return 0;
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java
new file mode 100644
index 0000000..3484dd8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/PatchSetNotificationSender.java
@@ -0,0 +1,149 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.google.gerrit.server.mail;
+
+import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromApprovals;
+import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
+
+import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.reviewdb.client.PatchSetInfo;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.index.ChangeIndexer;
+import com.google.gerrit.server.mail.MailUtil.MailRecipients;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.FooterLine;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+public class PatchSetNotificationSender {
+ private static final Logger log =
+ LoggerFactory.getLogger(PatchSetNotificationSender.class);
+
+ private final ReviewDb db;
+ private final GitRepositoryManager repoManager;
+ private final PatchSetInfoFactory patchSetInfoFactory;
+ private final ApprovalsUtil approvalsUtil;
+ private final AccountResolver accountResolver;
+ private final CreateChangeSender.Factory createChangeSenderFactory;
+ private final ReplacePatchSetSender.Factory replacePatchSetFactory;
+
+ @Inject
+ public PatchSetNotificationSender(ReviewDb db,
+ ChangeHooks hooks,
+ GitRepositoryManager repoManager,
+ PatchSetInfoFactory patchSetInfoFactory,
+ ApprovalsUtil approvalsUtil,
+ AccountResolver accountResolver,
+ CreateChangeSender.Factory createChangeSenderFactory,
+ ReplacePatchSetSender.Factory replacePatchSetFactory,
+ ChangeIndexer indexer) {
+ this.db = db;
+ this.repoManager = repoManager;
+ this.patchSetInfoFactory = patchSetInfoFactory;
+ this.approvalsUtil = approvalsUtil;
+ this.accountResolver = accountResolver;
+ this.createChangeSenderFactory = createChangeSenderFactory;
+ this.replacePatchSetFactory = replacePatchSetFactory;
+ }
+
+ public void send(final boolean newChange,
+ final IdentifiedUser currentUser, final Change updatedChange,
+ final PatchSet updatedPatchSet, final LabelTypes labelTypes)
+ throws OrmException, IOException, PatchSetInfoNotAvailableException {
+ final Repository git = repoManager.openRepository(updatedChange.getProject());
+ try {
+ final RevWalk revWalk = new RevWalk(git);
+ final RevCommit commit;
+ try {
+ commit = revWalk.parseCommit(ObjectId.fromString(
+ updatedPatchSet.getRevision().get()));
+ } finally {
+ revWalk.release();
+ }
+ final PatchSetInfo info = patchSetInfoFactory.get(commit, updatedPatchSet.getId());
+ final List<FooterLine> footerLines = commit.getFooterLines();
+ final Account.Id me = currentUser.getAccountId();
+ final MailRecipients recipients =
+ getRecipientsFromFooters(accountResolver, updatedPatchSet, footerLines);
+ recipients.remove(me);
+
+ if (newChange) {
+ approvalsUtil.addReviewers(db, labelTypes,
+ updatedChange, updatedPatchSet, info,
+ recipients.getReviewers(), Collections.<Account.Id> emptySet());
+ try {
+ CreateChangeSender cm = createChangeSenderFactory.create(updatedChange);
+ cm.setFrom(me);
+ cm.setPatchSet(updatedPatchSet, info);
+ cm.addReviewers(recipients.getReviewers());
+ cm.addExtraCC(recipients.getCcOnly());
+ cm.send();
+ } catch (Exception e) {
+ log.error("Cannot send email for new change " + updatedChange.getId(), e);
+ }
+ } else {
+ final List<PatchSetApproval> patchSetApprovals =
+ db.patchSetApprovals().byChange(
+ updatedChange.getId()).toList();
+ final MailRecipients oldRecipients =
+ getRecipientsFromApprovals(patchSetApprovals);
+ approvalsUtil.addReviewers(db, labelTypes, updatedChange,
+ updatedPatchSet, info, recipients.getReviewers(),
+ oldRecipients.getAll());
+ final ChangeMessage msg =
+ new ChangeMessage(new ChangeMessage.Key(updatedChange.getId(),
+ ChangeUtil.messageUUID(db)), me,
+ updatedPatchSet.getCreatedOn(), updatedPatchSet.getId());
+ msg.setMessage("Uploaded patch set " + updatedPatchSet.getPatchSetId() + ".");
+ try {
+ ReplacePatchSetSender cm = replacePatchSetFactory.create(updatedChange);
+ cm.setFrom(me);
+ cm.setPatchSet(updatedPatchSet, info);
+ cm.setChangeMessage(msg);
+ cm.addReviewers(recipients.getReviewers());
+ cm.addExtraCC(recipients.getCcOnly());
+ cm.send();
+ } catch (Exception e) {
+ log.error("Cannot send email for new patch set " + updatedPatchSet.getId(), e);
+ }
+ }
+ } finally {
+ git.close();
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java
index 0555fc7..5282b49 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java
@@ -162,12 +162,15 @@
}
private ChangeDataSource source() {
+ int minCost = Integer.MAX_VALUE;
+ Predicate<ChangeData> s = null;
for (Predicate<ChangeData> p : getChildren()) {
- if (p instanceof ChangeDataSource) {
- return (ChangeDataSource) p;
+ if (p instanceof ChangeDataSource && p.getCost() < minCost) {
+ s = p;
+ minCost = p.getCost();
}
}
- return null;
+ return (ChangeDataSource) s;
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index 72c7138..bdce7f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gson.Gson;
import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -229,7 +230,7 @@
final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible();
int cnt = queries.size();
- // Begin all queries, possibly asynchronously.
+ // Parse and rewrite all queries.
List<Integer> limits = Lists.newArrayListWithCapacity(cnt);
List<ChangeDataSource> sources = Lists.newArrayListWithCapacity(cnt);
for (int i = 0; i < cnt; i++) {
@@ -251,9 +252,16 @@
sources.add(a);
}
+ // Run each query asynchronously, if supported.
+ List<ResultSet<ChangeData>> matches = Lists.newArrayListWithCapacity(cnt);
+ for (ChangeDataSource s : sources) {
+ matches.add(s.read());
+ }
+ sources = null;
+
List<List<ChangeData>> out = Lists.newArrayListWithCapacity(cnt);
for (int i = 0; i < cnt; i++) {
- List<ChangeData> results = Lists.newArrayList(sources.get(i).read());
+ List<ChangeData> results = matches.get(i).toList();
Collections.sort(results, sortkeyAfter != null ? cmpAfter : cmpBefore);
if (results.size() > maxLimit) {
moreResults = true;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/IncludedInResolverTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/IncludedInResolverTest.java
new file mode 100644
index 0000000..ae58819
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/IncludedInResolverTest.java
@@ -0,0 +1,205 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.google.gerrit.server.change;
+
+import com.google.gerrit.common.data.IncludedInDetail;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class IncludedInResolverTest extends RepositoryTestCase {
+
+ // Branch names
+ private static final String BRANCH_MASTER = "master";
+ private static final String BRANCH_1_0 = "rel-1.0";
+ private static final String BRANCH_1_3 = "rel-1.3";
+ private static final String BRANCH_2_0 = "rel-2.0";
+ private static final String BRANCH_2_5 = "rel-2.5";
+
+ // Tag names
+ private static final String TAG_1_0 = "1.0";
+ private static final String TAG_1_0_1 = "1.0.1";
+ private static final String TAG_1_3 = "1.3";
+ private static final String TAG_2_0_1 = "2.0.1";
+ private static final String TAG_2_0 = "2.0";
+ private static final String TAG_2_5 = "2.5";
+ private static final String TAG_2_5_ANNOTATED = "2.5-annotated";
+ private static final String TAG_2_5_ANNOTATED_TWICE = "2.5-annotated_twice";
+
+ // Commits
+ private RevCommit commit_initial;
+ private RevCommit commit_v1_3;
+ private RevCommit commit_v2_5;
+
+ private List<String> expTags = new ArrayList<String>();
+ private List<String> expBranches = new ArrayList<String>();
+
+ private RevWalk revWalk;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ /*- The following graph will be created.
+
+ o tag 2.5, 2.5_annotated, 2.5_annotated_twice
+ |\
+ | o tag 2.0.1
+ | o tag 2.0
+ o | tag 1.3
+ |/
+ o c3
+
+ | o tag 1.0.1
+ |/
+ o tag 1.0
+ o c2
+ o c1
+
+ */
+
+ Git git = new Git(db);
+ revWalk = new RevWalk(db);
+ // Version 1.0
+ commit_initial = git.commit().setMessage("c1").call();
+ git.commit().setMessage("c2").call();
+ RevCommit commit_v1_0 = git.commit().setMessage("version 1.0").call();
+ git.tag().setName(TAG_1_0).setObjectId(commit_v1_0).call();
+ RevCommit c3 = git.commit().setMessage("c3").call();
+ // Version 1.01
+ createAndCheckoutBranch(commit_v1_0, BRANCH_1_0);
+ RevCommit commit_v1_0_1 =
+ git.commit().setMessage("verREFS_HEADS_RELsion 1.0.1").call();
+ git.tag().setName(TAG_1_0_1).setObjectId(commit_v1_0_1).call();
+ // Version 1.3
+ createAndCheckoutBranch(c3, BRANCH_1_3);
+ commit_v1_3 = git.commit().setMessage("version 1.3").call();
+ git.tag().setName(TAG_1_3).setObjectId(commit_v1_3).call();
+ // Version 2.0
+ createAndCheckoutBranch(c3, BRANCH_2_0);
+ RevCommit commit_v2_0 = git.commit().setMessage("version 2.0").call();
+ git.tag().setName(TAG_2_0).setObjectId(commit_v2_0).call();
+ RevCommit commit_v2_0_1 = git.commit().setMessage("version 2.0.1").call();
+ git.tag().setName(TAG_2_0_1).setObjectId(commit_v2_0_1).call();
+
+ // Version 2.5
+ createAndCheckoutBranch(commit_v1_3, BRANCH_2_5);
+ git.merge().include(commit_v2_0_1).setCommit(false)
+ .setFastForward(FastForwardMode.NO_FF).call();
+ commit_v2_5 = git.commit().setMessage("version 2.5").call();
+ git.tag().setName(TAG_2_5).setObjectId(commit_v2_5).setAnnotated(false)
+ .call();
+ Ref ref_tag_2_5_annotated =
+ git.tag().setName(TAG_2_5_ANNOTATED).setObjectId(commit_v2_5)
+ .setAnnotated(true).call();
+ RevTag tag_2_5_annotated =
+ revWalk.parseTag(ref_tag_2_5_annotated.getObjectId());
+ git.tag().setName(TAG_2_5_ANNOTATED_TWICE).setObjectId(tag_2_5_annotated)
+ .setAnnotated(true).call();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ revWalk.release();
+ super.tearDown();
+ }
+
+ @Test
+ public void resolveLatestCommit() throws Exception {
+ // Check tip commit
+ IncludedInDetail detail = resolve(commit_v2_5);
+
+ // Check that only tags and branches which refer the tip are returned
+ expTags.add(TAG_2_5);
+ expTags.add(TAG_2_5_ANNOTATED);
+ expTags.add(TAG_2_5_ANNOTATED_TWICE);
+ assertEquals(expTags, detail.getTags());
+ expBranches.add(BRANCH_2_5);
+ assertEquals(expBranches, detail.getBranches());
+ }
+
+ @Test
+ public void resolveFirstCommit() throws Exception {
+ // Check first commit
+ IncludedInDetail detail = resolve(commit_initial);
+
+ // Check whether all tags and branches are returned
+ expTags.add(TAG_1_0);
+ expTags.add(TAG_1_0_1);
+ expTags.add(TAG_1_3);
+ expTags.add(TAG_2_0);
+ expTags.add(TAG_2_0_1);
+ expTags.add(TAG_2_5);
+ expTags.add(TAG_2_5_ANNOTATED);
+ expTags.add(TAG_2_5_ANNOTATED_TWICE);
+ assertEquals(expTags, detail.getTags());
+
+ expBranches.add(BRANCH_MASTER);
+ expBranches.add(BRANCH_1_0);
+ expBranches.add(BRANCH_1_3);
+ expBranches.add(BRANCH_2_0);
+ expBranches.add(BRANCH_2_5);
+ assertEquals(expBranches, detail.getBranches());
+ }
+
+ @Test
+ public void resolveBetwixtCommit() throws Exception {
+ // Check a commit somewhere in the middle
+ IncludedInDetail detail = resolve(commit_v1_3);
+
+ // Check whether all succeeding tags and branches are returned
+ expTags.add(TAG_1_3);
+ expTags.add(TAG_2_5);
+ expTags.add(TAG_2_5_ANNOTATED);
+ expTags.add(TAG_2_5_ANNOTATED_TWICE);
+ assertEquals(expTags, detail.getTags());
+
+ expBranches.add(BRANCH_1_3);
+ expBranches.add(BRANCH_2_5);
+ assertEquals(expBranches, detail.getBranches());
+ }
+
+ private IncludedInDetail resolve(RevCommit commit) throws Exception {
+ return IncludedInResolver.resolve(db, revWalk, commit);
+ }
+
+ private void assertEquals(List<String> list1, List<String> list2) {
+ Collections.sort(list1);
+ Collections.sort(list2);
+ Assert.assertEquals(list1, list2);
+ }
+
+ private void createAndCheckoutBranch(ObjectId objectId, String branchName)
+ throws IOException {
+ String fullBranchName = "refs/heads/" + branchName;
+ super.createBranch(objectId, fullBranchName);
+ super.checkoutBranch(fullBranchName);
+ }
+}