Merge "Remove unused import."
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/AddApprovalsStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/AddApprovalsStep.java
index 8d91fb7..f5afd95 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/AddApprovalsStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/AddApprovalsStep.java
@@ -40,7 +40,7 @@
 class AddApprovalsStep {
 
   interface Factory {
-    AddApprovalsStep create(Change change, ChangeInfo changeInfo);
+    AddApprovalsStep create(Change change, ChangeInfo changeInfo, boolean resume);
   }
 
   private final AccountUtil accountUtil;
@@ -50,6 +50,7 @@
   private final ChangeControl.GenericFactory changeControlFactory;
   private final Change change;
   private final ChangeInfo changeInfo;
+  private final boolean resume;
 
   @Inject
   public AddApprovalsStep(AccountUtil accountUtil,
@@ -58,7 +59,8 @@
       IdentifiedUser.GenericFactory genericUserFactory,
       ChangeControl.GenericFactory changeControlFactory,
       @Assisted Change change,
-      @Assisted ChangeInfo changeInfo) {
+      @Assisted ChangeInfo changeInfo,
+      @Assisted boolean resume) {
     this.accountUtil = accountUtil;
     this.updateFactory = updateFactory;
     this.db = db;
@@ -66,10 +68,16 @@
     this.changeControlFactory = changeControlFactory;
     this.change = change;
     this.changeInfo = changeInfo;
+    this.resume = resume;
   }
 
   void add() throws OrmException, NoSuchChangeException, IOException,
       NoSuchAccountException {
+    if (resume) {
+      db.patchSetApprovals().delete(
+          db.patchSetApprovals().byChange(change.getId()));
+    }
+
     List<PatchSetApproval> approvals = new ArrayList<>();
     for (Entry<String, LabelInfo> e : changeInfo.labels.entrySet()) {
       String labelName = e.getKey();
@@ -83,12 +91,16 @@
               new PatchSetApproval.Key(change.currentPatchSetId(), user,
                   labelType.getLabelId()), a.value.shortValue(), a.date));
           ChangeUpdate update = updateFactory.create(ctrl);
-          update.putApproval(labelName, a.value.shortValue());
+          if (a.value != 0) {
+            update.putApproval(labelName, a.value.shortValue());
+          } else {
+            update.removeApproval(labelName);
+          }
           update.commit();
         }
       }
     }
-    db.patchSetApprovals().upsert(approvals);
+    db.patchSetApprovals().insert(approvals);
   }
 
   private ChangeControl control(Change change, AccountInfo acc)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java
index c302e79..e40fc5e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java
@@ -35,7 +35,7 @@
 class AddHashtagsStep {
 
   interface Factory {
-    AddHashtagsStep create(Change change, ChangeInfo changeInfo);
+    AddHashtagsStep create(Change change, ChangeInfo changeInfo, boolean resume);
   }
 
   private final HashtagsUtil hashtagsUtil;
@@ -43,26 +43,36 @@
   private final ChangeControl.GenericFactory changeControlFactory;
   private final Change change;
   private final ChangeInfo changeInfo;
+  private final boolean resume;
 
   @Inject
   AddHashtagsStep(HashtagsUtil hashtagsUtil,
       IdentifiedUser.GenericFactory genericUserFactory,
       ChangeControl.GenericFactory changeControlFactory,
       @Assisted Change change,
-      @Assisted ChangeInfo changeInfo) {
+      @Assisted ChangeInfo changeInfo,
+      @Assisted boolean resume) {
     this.hashtagsUtil = hashtagsUtil;
     this.change = change;
     this.changeInfo = changeInfo;
     this.genericUserFactory = genericUserFactory;
     this.changeControlFactory = changeControlFactory;
+    this.resume = resume;
   }
 
   void add() throws IllegalArgumentException, AuthException, IOException,
       ValidationException, OrmException, NoSuchChangeException {
+    ChangeControl ctrl = control(change, changeInfo.owner);
+
+    if (resume) {
+      HashtagsInput input = new HashtagsInput();
+      input.remove = ctrl.getNotes().load().getHashtags();
+      hashtagsUtil.setHashtags(ctrl, input, false, false);
+    }
+
     HashtagsInput input = new HashtagsInput();
     input.add = new HashSet<>(changeInfo.hashtags);
-    hashtagsUtil.setHashtags(control(change, changeInfo.owner),
-        input, false, false);
+    hashtagsUtil.setHashtags(ctrl, input, false, false);
   }
 
   private ChangeControl control(Change change, AccountInfo acc)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/GitFetchStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/GitFetchStep.java
index 4ebf8fd..10e0642 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/GitFetchStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/GitFetchStep.java
@@ -65,6 +65,7 @@
         case NEW:
         case FAST_FORWARD:
         case FORCED:
+        case NO_CHANGE:
           break;
         default:
           throw new IOException(String.format(
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportJson.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportJson.java
index ffc4f4a..b92db2b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportJson.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportJson.java
@@ -54,22 +54,28 @@
     this.accountLoaderFactory = accountLoaderFactory;
   }
 
-  public ImportProjectInfo format(Input input) throws OrmException {
-    ImportProjectInfo info = new ImportProjectInfo();
-    info.from = input.from;
-    info.parent = input.parent;
-    info.imports = new ArrayList<>();
+  public ImportProjectInfo format(Input input, ImportProjectInfo info)
+      throws OrmException {
+    if (info == null) {
+      info = new ImportProjectInfo();
+      info.from = input.from;
+      info.parent = input.parent;
+      info.imports = new ArrayList<>();
+    }
 
+    info.imports.add(createImportInfo(input.user));
+    return info;
+  }
+
+  private ImportInfo createImportInfo(String remoteUser) throws OrmException {
     AccountLoader accountLoader = accountLoaderFactory.create(true);
     ImportInfo importInfo = new ImportInfo();
     importInfo.timestamp = new Timestamp(TimeUtil.nowMs());
     importInfo.user =
         accountLoader.get(((IdentifiedUser) currentUser.get()).getAccountId());
-    importInfo.remoteUser = input.user;
-    info.imports.add(importInfo);
+    importInfo.remoteUser = remoteUser;
     accountLoader.fill();
-
-    return info;
+    return importInfo;
   }
 
   public static void persist(LockFile lockFile, ImportProjectInfo info,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java
index d666879..eb79abb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.extensions.annotations.PluginData;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -143,7 +142,7 @@
 
     LockFile lockFile = lockForImport(project);
     try {
-      return apply(lockFile, input, false);
+      return apply(lockFile, input, null);
     } finally {
       lockFile.unlock();
     }
@@ -162,35 +161,35 @@
       input.from = info.from;
       input.parent = info.parent;
 
-      return apply(lockFile, input, true);
+      return apply(lockFile, input, info);
     } finally {
       lockFile.unlock();
     }
   }
 
-  private Response<String> apply(LockFile lockFile, Input input, boolean resume)
+  private Response<String> apply(LockFile lockFile, Input input, ImportProjectInfo info)
       throws RestApiException, OrmException, IOException, ValidationException,
       GitAPIException, NoSuchChangeException, NoSuchAccountException {
-    if (resume) {
-      throw new NotImplementedException();
-    }
+    boolean resume = info != null;
 
     input.validate();
 
     ProgressMonitor pm = err != null ? new TextProgressMonitor(err) :
         NullProgressMonitor.INSTANCE;
 
+    checkProjectInSource(input, pm);
+
     try {
       setParentProjectName(input, pm);
       checkPreconditions(pm);
-      Repository repo = openRepoStep.open(project, pm);
+      Repository repo = openRepoStep.open(project, resume, pm);
       try {
-        ImportJson.persist(lockFile, importJson.format(input), pm);
+        ImportJson.persist(lockFile, importJson.format(input, info), pm);
         configRepoStep.configure(repo, project, input.from, pm);
         gitFetchStep.fetch(input.user, input.pass, repo, pm);
         configProjectStep.configure(project, parent, pm);
         replayChangesFactory.create(input.from, input.user, input.pass, repo,
-            project, pm)
+            project, resume, pm)
             .replay();
       } finally {
         repo.close();
@@ -211,6 +210,13 @@
     return Response.<String> ok("OK");
   }
 
+  private void checkProjectInSource(Input input, ProgressMonitor pm)
+      throws IOException, BadRequestException {
+    pm.beginTask("Check source project", 1);
+    new RemoteApi(input.from, input.user, input.pass).getProject(project.get());
+    updateAndEnd(pm);
+  }
+
   private void setParentProjectName(Input input, ProgressMonitor pm)
       throws IOException, BadRequestException {
     pm.beginTask("Set parent project", 1);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/InsertLinkToOriginalChangeStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/InsertLinkToOriginalChangeStep.java
index 581d383..fe04784 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/InsertLinkToOriginalChangeStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/InsertLinkToOriginalChangeStep.java
@@ -44,10 +44,11 @@
   private final String fromGerrit;
   private final Change change;
   private final ChangeInfo changeInfo;
+  private final boolean resume;
 
   interface Factory {
     InsertLinkToOriginalChangeStep create(String fromGerrit, Change change,
-        ChangeInfo changeInfo);
+        ChangeInfo changeInfo, boolean resume);
   }
 
   @Inject
@@ -59,7 +60,8 @@
       ChangeMessagesUtil cmUtil,
       @Assisted String fromGerrit,
       @Assisted Change change,
-      @Assisted ChangeInfo changeInfo) {
+      @Assisted ChangeInfo changeInfo,
+      @Assisted boolean resume) {
     this.currentUser = currentUser;
     this.updateFactory = updateFactory;
     this.genericUserFactory = genericUserFactory;
@@ -69,10 +71,12 @@
     this.fromGerrit = fromGerrit;
     this.change = change;
     this.changeInfo = changeInfo;
+    this.resume = resume;
   }
 
   void insert() throws NoSuchChangeException, OrmException, IOException {
-    insertMessage(change, "Imported from " + changeUrl(changeInfo));
+    insertMessage(change, (resume ? "Resumed import of " : "Imported from ")
+        + changeUrl(changeInfo));
   }
 
   private String changeUrl(ChangeInfo c) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/OpenRepositoryStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/OpenRepositoryStep.java
index e2e80c4..8b347a1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/OpenRepositoryStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/OpenRepositoryStep.java
@@ -50,15 +50,23 @@
     this.projectCreationValidationListeners = projectCreationValidationListeners;
   }
 
-  Repository open(Project.NameKey name, ProgressMonitor pm)
+  Repository open(Project.NameKey name, boolean resume, ProgressMonitor pm)
       throws ResourceConflictException, IOException {
     pm.beginTask("Open repository", 1);
     try {
-      git.openRepository(name);
-      throw new ResourceConflictException(format(
-          "repository %s already exists", name.get()));
+      Repository repo = git.openRepository(name);
+      if (resume) {
+        return repo;
+      } else {
+        throw new ResourceConflictException(format(
+            "repository %s already exists", name.get()));
+      }
     } catch (RepositoryNotFoundException e) {
       // Project doesn't exist
+      if (resume) {
+        throw new ResourceConflictException(format(
+            "repository %s does not exist", name.get()));
+      }
     }
 
     beforeCreateProject(name);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java b/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java
index 5d20f01..d48ca5d 100755
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java
@@ -105,6 +105,12 @@
       throw new BadRequestException(
           "invalid credentials: accessing source system failed with 401 Unauthorized");
     }
+    if (r.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
+      throw new BadRequestException(
+          "Project not found or missing permissions for accessing the project:"
+          + " accessing source system failed with 404 Not found");
+    }
+
     if (r.getStatusCode() < 200 || 300 <= r.getStatusCode()) {
       throw new IOException(String.format(
           "Unexpected response code for %s on %s : %s", method.name(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java
index f4c3b2b..42e1e43 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.importer;
 
+import com.google.common.collect.Iterators;
 import com.google.gerrit.common.errors.NoSuchAccountException;
 import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -24,9 +25,12 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 
 import org.eclipse.jgit.lib.Constants;
@@ -47,6 +51,7 @@
         @Assisted("password") String password,
         Repository repo,
         Project.NameKey name,
+        boolean resume,
         ProgressMonitor pm);
   }
 
@@ -59,11 +64,12 @@
   private final AccountUtil accountUtil;
   private final ReviewDb db;
   private final ChangeIndexer indexer;
+  private final Provider<InternalChangeQuery> queryProvider;
   private final String fromGerrit;
   private final RemoteApi api;
   private final Repository repo;
   private final Project.NameKey name;
-
+  private final boolean resume;
   private final ProgressMonitor pm;
 
   @Inject
@@ -77,12 +83,14 @@
       AccountUtil accountUtil,
       ReviewDb db,
       ChangeIndexer indexer,
-      @Assisted ProgressMonitor pm,
+      Provider<InternalChangeQuery> queryProvider,
       @Assisted("from") String fromGerrit,
       @Assisted("user") String user,
       @Assisted("password") String password,
       @Assisted Repository repo,
-      @Assisted Project.NameKey name) {
+      @Assisted Project.NameKey name,
+      @Assisted boolean resume,
+      @Assisted ProgressMonitor pm) {
     this.replayRevisionsFactory = replayRevisionsFactory;
     this.replayInlineCommentsFactory = replayInlineCommentsFactory;
     this.replayMessagesFactory = replayMessagesFactory;
@@ -92,11 +100,13 @@
     this.accountUtil = accountUtil;
     this.db = db;
     this.indexer = indexer;
+    this.queryProvider = queryProvider;
     this.fromGerrit = fromGerrit;
     this.api = new RemoteApi(fromGerrit, user, password);
     this.repo = repo;
     this.name = name;
     this.pm = pm;
+    this.resume = resume;
   }
 
   void replay() throws IOException, OrmException,
@@ -125,20 +135,40 @@
       return;
     }
 
-    Change change = createChange(c);
+    Change change = resume ? findChange(c) : null;
+    boolean resumeChange;
+    if (change == null) {
+      resumeChange = false;
+      change = createChange(c);
+    } else {
+      resumeChange = true;
+    }
     replayRevisionsFactory.create(repo, rw, change, c).replay();
-    db.changes().insert(Collections.singleton(change));
+    upsertChange(resumeChange, change, c);
 
-    replayInlineCommentsFactory.create(change, c, api).replay();
-    replayMessagesFactory.create(change, c).replay();
-    addApprovalsFactory.create(change, c).add();
-    addHashtagsFactory.create(change, c).add();
+    replayInlineCommentsFactory.create(change, c, api, resumeChange).replay();
+    replayMessagesFactory.create(change, c, resumeChange).replay();
+    addApprovalsFactory.create(change, c, resume).add();
+    addHashtagsFactory.create(change, c, resumeChange).add();
 
-    insertLinkToOriginalFactory.create(fromGerrit,change, c).insert();
+    insertLinkToOriginalFactory.create(fromGerrit,change, c, resumeChange).insert();
 
     indexer.index(db, change);
   }
 
+  private Change findChange(ChangeInfo c) throws OrmException {
+    List<Change> changes = ChangeData.asChanges(
+        queryProvider.get().byBranchKey(
+            new Branch.NameKey(name, fullName(c.branch)),
+            new Change.Key(c.changeId)));
+    if (changes.isEmpty()) {
+      return null;
+    } else {
+      return db.changes().get(
+          Iterators.getOnlyElement(changes.iterator()).getId());
+    }
+  }
+
   private Change createChange(ChangeInfo c) throws OrmException,
       NoSuchAccountException {
     Change.Id changeId = new Change.Id(db.nextChangeId());
@@ -153,6 +183,16 @@
     return change;
   }
 
+  private void upsertChange(boolean resumeChange, Change change, ChangeInfo c)
+      throws OrmException {
+    if (resumeChange) {
+      change.setStatus(Change.Status.forChangeStatus(c.status));
+      change.setTopic(c.topic);
+      change.setLastUpdatedOn(c.updated);
+    }
+    db.changes().upsert(Collections.singleton(change));
+  }
+
   private static String fullName(String branch) {
     if (branch.startsWith(Constants.R_HEADS)) {
       return branch;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayInlineCommentsStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayInlineCommentsStep.java
index 5037f8e..1e2fa12 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayInlineCommentsStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayInlineCommentsStep.java
@@ -46,15 +46,17 @@
 
 import java.io.IOException;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 class ReplayInlineCommentsStep {
 
   interface Factory {
     ReplayInlineCommentsStep create(Change change, ChangeInfo changeInfo,
-        RemoteApi api);
+        RemoteApi api, boolean resume);
   }
 
   private final AccountUtil accountUtil;
@@ -67,6 +69,7 @@
   private final Change change;
   private final ChangeInfo changeInfo;
   private final RemoteApi api;
+  private final boolean resume;
 
   @Inject
   public ReplayInlineCommentsStep(AccountUtil accountUtil,
@@ -78,7 +81,8 @@
       PatchListCache patchListCache,
       @Assisted Change change,
       @Assisted ChangeInfo changeInfo,
-      @Assisted RemoteApi api) {
+      @Assisted RemoteApi api,
+      @Assisted boolean resume) {
     this.accountUtil = accountUtil;
     this.db = db;
     this.genericUserFactory = genericUserFactory;
@@ -89,6 +93,7 @@
     this.change = change;
     this.changeInfo = changeInfo;
     this.api = api;
+    this.resume = resume;
   }
 
   void replay() throws RestApiException, OrmException, IOException,
@@ -96,6 +101,9 @@
     for (PatchSet ps : db.patchSets().byChange(change.getId())) {
       Iterable<CommentInfo> comments = api.getComments(
           changeInfo._number, ps.getRevision().get());
+      if (resume) {
+        comments = filterComments(ps, comments);
+      }
 
       Multimap<Account.Id, CommentInfo> commentsByAuthor = ArrayListMultimap.create();
       for (CommentInfo comment : comments) {
@@ -109,7 +117,24 @@
     }
   }
 
-  private boolean insertComments(PatchSet ps, Account.Id author,
+  private Iterable<CommentInfo> filterComments(PatchSet ps,
+      Iterable<CommentInfo> comments) throws OrmException {
+    Set<String> existingUuids = new HashSet<>();
+    for (PatchLineComment c : db.patchComments().byPatchSet(ps.getId())) {
+      existingUuids.add(c.getKey().get());
+    }
+
+    Iterator<CommentInfo> it = comments.iterator();
+    while (it.hasNext()) {
+      CommentInfo c = it.next();
+      if (existingUuids.contains(Url.decode(c.id))) {
+        it.remove();
+      }
+    }
+    return comments;
+  }
+
+  private void insertComments(PatchSet ps, Account.Id author,
       Collection<CommentInfo> comments) throws OrmException, IOException,
       NoSuchChangeException {
     ChangeControl ctrl = control(change, author);
@@ -154,10 +179,6 @@
     plcUtil.deleteComments(db, update, del);
     plcUtil.upsertComments(db, update, ups);
     update.commit();
-
-    db.changes().update(Collections.singleton(change));
-
-    return !del.isEmpty() || !ups.isEmpty();
   }
 
   private Map<String, PatchLineComment> scanDraftComments(ChangeControl ctrl,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayMessagesStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayMessagesStep.java
index f2a705d..1b2d5e8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayMessagesStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayMessagesStep.java
@@ -38,7 +38,8 @@
 class ReplayMessagesStep {
 
   interface Factory {
-    ReplayMessagesStep create(Change change, ChangeInfo changeInfo);
+    ReplayMessagesStep create(Change change, ChangeInfo changeInfo,
+        boolean resume);
   }
 
   private final AccountUtil accountUtil;
@@ -49,6 +50,7 @@
   private final ChangeControl.GenericFactory changeControlFactory;
   private final Change change;
   private final ChangeInfo changeInfo;
+  private final boolean resume;
 
   @Inject
   public ReplayMessagesStep(AccountUtil accountUtil,
@@ -58,7 +60,8 @@
       ChangeControl.GenericFactory changeControlFactory,
       ReviewDb db,
       @Assisted Change change,
-      @Assisted ChangeInfo changeInfo) {
+      @Assisted ChangeInfo changeInfo,
+      @Assisted boolean resume) {
     this.accountUtil = accountUtil;
     this.updateFactory = updateFactory;
     this.cmUtil = cmUtil;
@@ -67,18 +70,25 @@
     this.changeControlFactory = changeControlFactory;
     this.change = change;
     this.changeInfo = changeInfo;
+    this.resume = resume;
   }
 
   void replay() throws NoSuchAccountException, NoSuchChangeException,
       OrmException, IOException {
     for (ChangeMessageInfo msg : changeInfo.messages) {
+      ChangeMessage.Key msgKey = new ChangeMessage.Key(change.getId(), msg.id);
+      if (resume && db.changeMessages().get(msgKey) != null) {
+        // already replayed
+        continue;
+      }
+
       Timestamp ts = msg.date;
       if (msg.author != null) {
         Account.Id userId = accountUtil.resolveUser(msg.author);
         ChangeUpdate update = updateFactory.create(control(change, userId), ts);
         ChangeMessage cmsg =
-            new ChangeMessage(new ChangeMessage.Key(change.getId(), msg.id),
-                userId, ts, new PatchSet.Id(change.getId(), msg._revisionNumber));
+            new ChangeMessage(msgKey, userId, ts, new PatchSet.Id(
+                change.getId(), msg._revisionNumber));
         cmsg.setMessage(msg.message);
         cmUtil.addChangeMessage(db, update, cmsg);
         update.commit();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayRevisionsStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayRevisionsStep.java
index 8e3762c..8d5b152 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayRevisionsStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayRevisionsStep.java
@@ -85,15 +85,20 @@
           // no import of draft patch sets
           continue;
         }
+        PatchSet ps = new PatchSet(new PatchSet.Id(change.getId(), r._number));
+        String newRef = ps.getId().toRefName();
+        if (repo.resolve(newRef) != null) {
+          // already replayed
+          continue;
+        }
+
         String origRef = imported(r.ref);
         ObjectId id = repo.resolve(origRef);
         if (id == null) {
-          // already replayed?
           continue;
         }
         RevCommit commit = rw.parseCommit(id);
 
-        PatchSet ps = new PatchSet(new PatchSet.Id(change.getId(), r._number));
         patchSets.add(ps);
 
         ps.setUploader(accountUtil.resolveUser(r.uploader));