Merge "Load an arbitrary version of change notes" into stable-3.3
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index a5acbe3..e81160a 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -118,9 +118,10 @@
   private ObjectId revision;
   private boolean loaded;
 
-  protected AbstractChangeNotes(Args args, Change.Id changeId) {
+  protected AbstractChangeNotes(Args args, Change.Id changeId, @Nullable ObjectId metaSha1) {
     this.args = requireNonNull(args);
     this.changeId = requireNonNull(changeId);
+    this.revision = metaSha1;
   }
 
   public Change.Id getChangeId() {
@@ -152,7 +153,7 @@
     try (Timer0.Context timer = args.metrics.readLatency.start();
         // Call openHandle even if reading is disabled, to trigger
         // auto-rebuilding before this object may get passed to a ChangeUpdate.
-        LoadHandle handle = openHandle(repo)) {
+        LoadHandle handle = openHandle(repo, revision)) {
       revision = handle.id();
       onLoad(handle);
       loaded = true;
@@ -174,15 +175,16 @@
    * <p>Implementations may override this method to provide auto-rebuilding behavior.
    *
    * @param repo open repository.
+   * @param id version SHA1 of the change notes to load
    * @return handle for reading the entity.
    * @throws NoSuchChangeException change does not exist.
    * @throws IOException a repo-level error occurred.
    */
-  protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
-    return openHandle(repo, readRef(repo));
-  }
-
-  protected LoadHandle openHandle(Repository repo, ObjectId id) {
+  protected LoadHandle openHandle(Repository repo, @Nullable ObjectId id)
+      throws NoSuchChangeException, IOException {
+    if (id == null) {
+      id = readRef(repo);
+    }
     return new LoadHandle(repo, id);
   }
 
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 9509568..8eb5c5c 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -110,9 +110,23 @@
       return createChecked(c.getProject(), c.getId());
     }
 
-    public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+    public ChangeNotes createChecked(
+        Repository repo,
+        Project.NameKey project,
+        Change.Id changeId,
+        @Nullable ObjectId metaRevId) {
       Change change = newChange(project, changeId);
-      return new ChangeNotes(args, change, true, null).load();
+      return new ChangeNotes(args, change, true, null, metaRevId).load(repo);
+    }
+
+    public ChangeNotes createChecked(
+        Project.NameKey project, Change.Id changeId, @Nullable ObjectId metaRevId) {
+      Change change = newChange(project, changeId);
+      return new ChangeNotes(args, change, true, null, metaRevId).load();
+    }
+
+    public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+      return createChecked(project, changeId, null);
     }
 
     public static Change newChange(Project.NameKey project, Change.Id changeId) {
@@ -344,14 +358,23 @@
   private ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals;
   private ImmutableSet<Comment.Key> commentKeys;
 
-  @VisibleForTesting
-  public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
-    super(args, change.getId());
+  public ChangeNotes(
+      Args args,
+      Change change,
+      boolean shouldExist,
+      @Nullable RefCache refs,
+      @Nullable ObjectId metaSha1) {
+    super(args, change.getId(), metaSha1);
     this.change = new Change(change);
     this.shouldExist = shouldExist;
     this.refs = refs;
   }
 
+  @VisibleForTesting
+  public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
+    this(args, change, shouldExist, refs, null);
+  }
+
   public Change getChange() {
     return change;
   }
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 9b403e8..4988406 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -59,7 +59,7 @@
   }
 
   DraftCommentNotes(Args args, Change.Id changeId, Account.Id author, @Nullable Ref ref) {
-    super(args, changeId);
+    super(args, changeId, null);
     this.author = requireNonNull(author);
     this.ref = ref;
     if (ref != null) {
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
index fe05643..d53b2ca 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
@@ -47,7 +47,7 @@
 
   @Inject
   RobotCommentNotes(Args args, @Assisted Change change) {
-    super(args, change.getId());
+    super(args, change.getId(), null);
     this.change = change;
   }