ExportReviewNotes: Dump submitted changes to refs/notes/review
This program allows site administrators to dump their existing notes out to the refs/notes/review branch, making the prior data available to Git clients. Change-Id: Iebaf1e4b2fb4620443e80d2a8f840cb30ae1e389 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
// Copyright (C) 2010 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.pgm;
|
||||
|
||||
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
|
||||
|
||||
import com.google.gerrit.common.data.ApprovalTypes;
|
||||
import com.google.gerrit.lifecycle.LifecycleManager;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.pgm.util.SiteProgram;
|
||||
import com.google.gerrit.reviewdb.Change;
|
||||
import com.google.gerrit.reviewdb.PatchSet;
|
||||
import com.google.gerrit.reviewdb.Project;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.GerritPersonIdentProvider;
|
||||
import com.google.gerrit.server.account.AccountCacheImpl;
|
||||
import com.google.gerrit.server.account.GroupCacheImpl;
|
||||
import com.google.gerrit.server.cache.CachePool;
|
||||
import com.google.gerrit.server.config.ApprovalTypesProvider;
|
||||
import com.google.gerrit.server.config.AuthConfigModule;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
|
||||
import com.google.gerrit.server.config.FactoryModule;
|
||||
import com.google.gerrit.server.git.CodeReviewNoteCreationException;
|
||||
import com.google.gerrit.server.git.CreateCodeReviewNotes;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Scopes;
|
||||
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.TextProgressMonitor;
|
||||
import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
|
||||
import org.eclipse.jgit.util.BlockList;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/** Export review notes for all submitted changes in all projects. */
|
||||
public class ExportReviewNotes extends SiteProgram {
|
||||
@Option(name = "--threads", usage = "Number of concurrent threads to run")
|
||||
private int threads = 2 * Runtime.getRuntime().availableProcessors();
|
||||
|
||||
private final LifecycleManager manager = new LifecycleManager();
|
||||
private final TextProgressMonitor textMonitor = new TextProgressMonitor();
|
||||
private final ThreadSafeProgressMonitor monitor =
|
||||
new ThreadSafeProgressMonitor(textMonitor);
|
||||
|
||||
private Injector dbInjector;
|
||||
private Injector gitInjector;
|
||||
|
||||
@Inject
|
||||
private GitRepositoryManager gitManager;
|
||||
|
||||
@Inject
|
||||
private SchemaFactory<ReviewDb> database;
|
||||
|
||||
@Inject
|
||||
private CreateCodeReviewNotes.Factory codeReviewNotesFactory;
|
||||
|
||||
private Map<Project.NameKey, List<Change>> changes;
|
||||
|
||||
@Override
|
||||
public int run() throws Exception {
|
||||
if (threads <= 0) {
|
||||
threads = 1;
|
||||
}
|
||||
|
||||
dbInjector = createDbInjector(MULTI_USER);
|
||||
gitInjector = dbInjector.createChildInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
|
||||
bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in(
|
||||
Scopes.SINGLETON);
|
||||
bind(String.class).annotatedWith(CanonicalWebUrl.class)
|
||||
.toProvider(CanonicalWebUrlProvider.class).in(Scopes.SINGLETON);
|
||||
bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class)
|
||||
.toProvider(GerritPersonIdentProvider.class).in(Scopes.SINGLETON);
|
||||
bind(CachePool.class);
|
||||
|
||||
install(AccountCacheImpl.module());
|
||||
install(GroupCacheImpl.module());
|
||||
install(new AuthConfigModule());
|
||||
install(new FactoryModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
factory(CreateCodeReviewNotes.Factory.class);
|
||||
}
|
||||
});
|
||||
install(new LifecycleModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
listener().to(CachePool.Lifecycle.class);
|
||||
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
manager.add(dbInjector, gitInjector);
|
||||
manager.start();
|
||||
gitInjector.injectMembers(this);
|
||||
|
||||
List<Change> allChangeList = allChanges();
|
||||
monitor.beginTask("Scanning changes", allChangeList.size());
|
||||
changes = cluster(allChangeList);
|
||||
allChangeList = null;
|
||||
|
||||
monitor.startWorkers(threads);
|
||||
for (int tid = 0; tid < threads; tid++) {
|
||||
new Worker().start();
|
||||
}
|
||||
monitor.waitForCompletion();
|
||||
monitor.endTask();
|
||||
manager.stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private List<Change> allChanges() throws OrmException {
|
||||
final ReviewDb db = database.open();
|
||||
try {
|
||||
return db.changes().all().toList();
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Project.NameKey, List<Change>> cluster(List<Change> changes) {
|
||||
HashMap<Project.NameKey, List<Change>> m =
|
||||
new HashMap<Project.NameKey, List<Change>>();
|
||||
for (Change change : changes) {
|
||||
if (change.getStatus() == Change.Status.MERGED) {
|
||||
List<Change> l = m.get(change.getProject());
|
||||
if (l == null) {
|
||||
l = new BlockList<Change>();
|
||||
m.put(change.getProject(), l);
|
||||
}
|
||||
l.add(change);
|
||||
} else {
|
||||
monitor.update(1);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private void export(ReviewDb db, Project.NameKey project, List<Change> changes)
|
||||
throws IOException, OrmException, CodeReviewNoteCreationException,
|
||||
InterruptedException {
|
||||
final Repository git;
|
||||
try {
|
||||
git = gitManager.openRepository(project);
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CreateCodeReviewNotes notes = codeReviewNotesFactory.create(db, git);
|
||||
try {
|
||||
notes.loadBase();
|
||||
for (Change change : changes) {
|
||||
monitor.update(1);
|
||||
PatchSet ps = db.patchSets().get(change.currentPatchSetId());
|
||||
if (ps == null) {
|
||||
continue;
|
||||
}
|
||||
notes.add(change, ObjectId.fromString(ps.getRevision().get()));
|
||||
}
|
||||
notes.commit("Exported prior reviews from Gerrit Code Review\n");
|
||||
notes.updateRef();
|
||||
} finally {
|
||||
notes.release();
|
||||
}
|
||||
} finally {
|
||||
git.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Map.Entry<Project.NameKey, List<Change>> next() {
|
||||
synchronized (changes) {
|
||||
if (changes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Project.NameKey name = changes.keySet().iterator().next();
|
||||
final List<Change> list = changes.remove(name);
|
||||
return new Map.Entry<Project.NameKey, List<Change>>() {
|
||||
@Override
|
||||
public Project.NameKey getKey() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Change> getValue() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Change> setValue(List<Change> value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class Worker extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
ReviewDb db;
|
||||
try {
|
||||
db = database.open();
|
||||
} catch (OrmException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (;;) {
|
||||
Entry<Project.NameKey, List<Change>> next = next();
|
||||
if (next != null) {
|
||||
try {
|
||||
export(db, next.getKey(), next.getValue());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (OrmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CodeReviewNoteCreationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
monitor.endWorker();
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user