/**
* Copyright (c) 2010, 2014 Darmstadt University of Technology.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marcel Bruch - initial API and implementation.
*/
package org.eclipse.recommenders.internal.snipmatch.rcp;
import static java.text.MessageFormat.format;
import static org.apache.commons.lang3.StringUtils.abbreviate;
import static org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR;
import static org.eclipse.recommenders.utils.Constants.EXT_JSON;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.IteratorService;
import org.eclipse.egit.core.internal.job.JobUtil;
import org.eclipse.egit.core.op.CommitOperation;
import org.eclipse.egit.core.op.PushOperation;
import org.eclipse.egit.core.op.PushOperationResult;
import org.eclipse.egit.core.op.ResetOperation;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.commit.CommitHelper;
import org.eclipse.egit.ui.internal.commit.CommitJob;
import org.eclipse.egit.ui.internal.dialogs.CommitDialog;
import org.eclipse.egit.ui.internal.push.SimpleConfigurePushDialog;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.lib.IndexDiff;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.util.StringUtils;
import org.eclipse.recommenders.injection.InjectionService;
import org.eclipse.recommenders.internal.snipmatch.rcp.Repositories.SnippetRepositoryConfigurationChangedEvent;
import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.LogMessages;
import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.Messages;
import org.eclipse.recommenders.snipmatch.GitSnippetRepository;
import org.eclipse.recommenders.snipmatch.GitSnippetRepository.GitNoCurrentFormatBranchException;
import org.eclipse.recommenders.snipmatch.GitSnippetRepository.GitNoFormatBranchException;
import org.eclipse.recommenders.snipmatch.GitSnippetRepository.GitUpdateException;
import org.eclipse.recommenders.snipmatch.ISearchContext;
import org.eclipse.recommenders.snipmatch.ISnippet;
import org.eclipse.recommenders.snipmatch.ISnippetRepository;
import org.eclipse.recommenders.snipmatch.Snippet;
import org.eclipse.recommenders.snipmatch.model.SnippetRepositoryConfiguration;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryClosedEvent;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryContentChangedEvent;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryOpenedEvent;
import org.eclipse.recommenders.snipmatch.rcp.model.EclipseGitSnippetRepositoryConfiguration;
import org.eclipse.recommenders.utils.Logs;
import org.eclipse.recommenders.utils.Recommendation;
import org.eclipse.recommenders.utils.Uris;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.name.Names;
@SuppressWarnings("restriction")
public class EclipseGitSnippetRepository implements ISnippetRepository {
private static final String SNIPPETS_DIR = "snippets/"; //$NON-NLS-1$
private static final int COMMIT_MESSAGE_FIRST_LINE_LENGTH = 65;
private static final int COMMIT_MESSAGE_LINE_LENGTH = 70;
private final EventBus bus;
private final GitSnippetRepository delegate;
private volatile int timesOpened;
private volatile boolean delegateOpen;
private final Lock readLock;
private final Lock writeLock;
private volatile Job openJob = null;
private final ISchedulingRule schedulingRule = new ISchedulingRule() {
@Override
public boolean isConflicting(ISchedulingRule rule) {
return rule == this;
}
@Override
public boolean contains(ISchedulingRule rule) {
return rule == this;
}
};
private Set<String> notTracked;
private Set<String> files;
public EclipseGitSnippetRepository(String id, File basedir, URI fetchUri, URI pushUri, String pushBranchPrefix,
EventBus bus) {
this.bus = bus;
delegate = new GitSnippetRepository(id, new File(basedir, Uris.mangle(fetchUri)), fetchUri, pushUri,
pushBranchPrefix);
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readLock = readWriteLock.readLock();
writeLock = readWriteLock.writeLock();
}
@Override
public void open() {
writeLock.lock();
try {
timesOpened++;
if (timesOpened > 1) {
return;
}
if (openJob == null && !delegateOpen) {
openJob = new Job(Messages.JOB_NAME_OPENING_SNIPPET_REPOSITORY) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
delegate.open();
changeStateToOpen();
return Status.OK_STATUS;
} catch (GitUpdateException e) {
changeStateToOpen();
Status status = new Status(IStatus.WARNING, Constants.BUNDLE_ID,
MessageFormat.format(Messages.WARNING_FAILURE_TO_UPDATE_REPOSITORY,
delegate.getRepositoryLocation(), e.getMessage()),
e);
Platform.getLog(Platform.getBundle(Constants.BUNDLE_ID)).log(status);
return Status.OK_STATUS;
} catch (final GitNoCurrentFormatBranchException e) {
changeStateToOpen();
Status status = new Status(IStatus.WARNING, Constants.BUNDLE_ID,
MessageFormat.format(Messages.WARNING_FAILURE_TO_CHECKOUT_CURRENT_BRANCH,
Snippet.FORMAT_VERSION, delegate.getRepositoryLocation(),
e.getCheckoutVersion(), e.getMessage()),
e);
Platform.getLog(Platform.getBundle(Constants.BUNDLE_ID)).log(status);
final Display display = Display.getDefault();
display.asyncExec(new Runnable() {
@Override
public void run() {
BranchCheckoutFailureDialog dialog = new BranchCheckoutFailureDialog(
display.getActiveShell(), delegate.getRepositoryLocation(),
Snippet.FORMAT_VERSION, e.getCheckoutVersion());
dialog.open();
}
});
return Status.OK_STATUS;
} catch (final GitNoFormatBranchException e) {
Logs.log(LogMessages.ERROR_FAILED_TO_OPEN_GIT_SNIPPET_REPOSITORY, e);
Status status = new Status(IStatus.ERROR,
Constants.BUNDLE_ID, MessageFormat.format(Messages.ERROR_NO_FORMAT_BRANCH,
Snippet.FORMAT_VERSION, delegate.getRepositoryLocation(), e.getMessage()),
e);
Platform.getLog(Platform.getBundle(Constants.BUNDLE_ID)).log(status);
final Display display = Display.getDefault();
display.asyncExec(new Runnable() {
@Override
public void run() {
BranchCheckoutFailureDialog dialog = new BranchCheckoutFailureDialog(
display.getActiveShell(), delegate.getRepositoryLocation(),
Snippet.FORMAT_VERSION);
dialog.open();
}
});
return Status.CANCEL_STATUS;
} catch (IOException e) {
Logs.log(LogMessages.ERROR_FAILED_TO_OPEN_GIT_SNIPPET_REPOSITORY, e);
Status status = new Status(IStatus.ERROR, Constants.BUNDLE_ID,
MessageFormat.format(Messages.ERROR_FAILURE_TO_CLONE_REPOSITORY,
delegate.getRepositoryLocation(), timesOpened, e.getMessage()),
e);
Platform.getLog(Platform.getBundle(Constants.BUNDLE_ID)).log(status);
return Status.CANCEL_STATUS;
} finally {
openJob = null;
}
}
private void changeStateToOpen() {
delegateOpen = true;
bus.post(new SnippetRepositoryOpenedEvent(EclipseGitSnippetRepository.this));
}
};
openJob.schedule();
}
} finally {
writeLock.unlock();
}
}
@Override
public void close() throws IOException {
writeLock.lock();
try {
if (timesOpened == 0) {
return;
} else if (timesOpened > 1) {
timesOpened--;
return;
} else if (timesOpened == 1) {
timesOpened = 0;
if (openJob != null) {
try {
openJob.join();
openJob = null;
} catch (InterruptedException e) {
Logs.log(LogMessages.ERROR_FAILED_TO_JOIN_OPEN_JOB, e);
}
}
delegate.close();
delegateOpen = false;
bus.post(new SnippetRepositoryClosedEvent(this));
}
} finally {
writeLock.unlock();
}
}
@Override
public List<Recommendation<ISnippet>> search(ISearchContext context) {
readLock.lock();
try {
if (!isOpen() || !delegateOpen) {
return Collections.emptyList();
}
return delegate.search(context);
} finally {
readLock.unlock();
}
}
@Override
public List<Recommendation<ISnippet>> search(ISearchContext context, int maxResults) {
readLock.lock();
try {
if (!isOpen() || !delegateOpen) {
return Collections.emptyList();
}
return delegate.search(context, maxResults);
} finally {
readLock.unlock();
}
}
@Subscribe
public void onEvent(SnippetRepositoryConfigurationChangedEvent e) throws IOException {
close();
open();
}
@Override
public String getRepositoryLocation() {
readLock.lock();
try {
return delegate.getRepositoryLocation();
} finally {
readLock.unlock();
}
}
@Override
public String getId() {
readLock.lock();
try {
return delegate.getId();
} finally {
readLock.unlock();
}
}
@Override
public boolean hasSnippet(UUID uuid) {
readLock.lock();
try {
if (!isOpen() || !delegateOpen) {
return false;
}
return delegate.hasSnippet(uuid);
} finally {
readLock.unlock();
}
}
@Override
public boolean delete(UUID uuid) throws IOException {
writeLock.lock();
try {
if (!isOpen() || !delegateOpen) {
return false;
}
boolean deleted = delegate.delete(uuid);
if (deleted) {
bus.post(new SnippetRepositoryContentChangedEvent(this));
}
return deleted;
} finally {
writeLock.unlock();
}
}
@Override
public boolean isDeleteSupported() {
return delegate.isDeleteSupported();
}
private boolean isOpen() {
return timesOpened > 0;
}
@Override
public void importSnippet(ISnippet snippet) throws IOException {
writeLock.lock();
try {
Preconditions.checkState(isOpen(), Messages.ERROR_REPOSITORY_NOT_OPEN_YET);
delegate.importSnippet(snippet);
bus.post(new SnippetRepositoryContentChangedEvent(this));
} finally {
writeLock.unlock();
}
}
@Override
public boolean isImportSupported() {
return delegate.isImportSupported();
}
public static ISnippetRepository createRepositoryInstance(EclipseGitSnippetRepositoryConfiguration config) {
EventBus bus = InjectionService.getInstance().requestInstance(EventBus.class);
File basedir = InjectionService.getInstance().requestAnnotatedInstance(File.class,
Names.named(SnipmatchRcpModule.SNIPPET_REPOSITORY_BASEDIR));
URI uri = Uris.toUri(config.getUrl());
URI pushUri = Uris.toUri(config.getPushUrl());
return new EclipseGitSnippetRepository(config.getId(), basedir, uri, pushUri, config.getPushBranchPrefix(),
bus);
}
public static BasicEList<SnippetRepositoryConfiguration> getDefaultConfiguration() {
BasicEList<SnippetRepositoryConfiguration> result = new BasicEList<SnippetRepositoryConfiguration>();
result.addAll(DefaultGitSnippetRepositoryConfigurations.fetchDefaultConfigurations());
return result;
}
@Override
public boolean delete() {
writeLock.lock();
try {
try {
close();
delegate.delete();
return true;
} catch (IOException e) {
Logs.log(LogMessages.ERROR_FAILED_TO_DELETE_GIT_SNIPPET_REPOSITORY_ON_DISK, e);
return false;
}
} finally {
writeLock.unlock();
}
}
@Override
public boolean share(Collection<UUID> uuids) {
writeLock.lock();
try {
Collection<ISnippet> snippets = Lists.newArrayList();
for (UUID uuid : uuids) {
ISnippet snippet = delegate.getSnippet(uuid);
if (snippet != null) {
snippets.add(snippet);
}
}
return shareSnippets(snippets);
} finally {
writeLock.unlock();
}
}
private boolean shareSnippets(Collection<ISnippet> snippets) {
if (snippets.isEmpty()) {
return false;
}
final Workspace workspace = (Workspace) ResourcesPlugin.getWorkspace();
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
List<IResource> resources = toResource(snippets, workspace);
IResource[] selectedResources = resources.toArray(new IResource[resources.size()]);
boolean committed = commit(delegate.getGitRepo(), selectedResources, workspace.getRoot(), snippets, shell);
if (!committed) {
return false;
}
push(delegate.getGitRepo(), shell);
reset(workspace);
return true;
}
private List<IResource> toResource(Collection<ISnippet> snippets, Workspace workspace) {
List<IResource> resources = Lists.newArrayList();
for (ISnippet snippet : snippets) {
File snippetFile = delegate.getSnippetFile(snippet.getUuid());
if (snippetFile == null) {
continue;
}
IPath location = new Path(snippetFile.getAbsolutePath());
IResource file = workspace.newResource(location, IResource.FILE);
resources.add(file);
}
return resources;
}
@Override
public boolean isSharingSupported() {
return true;
}
private boolean commit(Repository repo, IResource[] selectedResources, IResource workspace,
Collection<ISnippet> snippets, Shell shell) {
files = Sets.newHashSet();
IndexDiff indexDiff = null;
try {
indexDiff = buildIndexHeadDiffList(delegate.getGitRepo(), new NullProgressMonitor());
} catch (OperationCanceledException e) {
return false;
} catch (IOException e) {
Logs.log(LogMessages.ERROR_CREATING_INDEX_HEAD_DIFF, delegate.getRepositoryLocation());
return false;
}
if (indexDiff == null) {
Logs.log(LogMessages.ERROR_CREATING_INDEX_HEAD_DIFF, delegate.getRepositoryLocation());
}
if (files.isEmpty()) {
MessageDialog.openInformation(shell, Messages.DIALOG_TITLE_SELECTION_NOT_SHAREABLE,
Messages.DIALOG_MESSAGE_NO_GIT_CHANGES_IN_SELECTION);
return false;
}
final Set<String> preselectedFiles = Sets.newHashSet();
getPreselectedFiles(selectedResources, preselectedFiles);
if (preselectedFiles.isEmpty()) {
MessageDialog.openInformation(shell, Messages.DIALOG_TITLE_SELECTION_NOT_SHAREABLE,
Messages.DIALOG_MESSAGE_NO_GIT_CHANGES_IN_SELECTION);
return false;
}
Collection<ISnippet> changedSnippets = getChangedSnippets(snippets, preselectedFiles);
String commitMessage = getCommitMessage(changedSnippets);
CommitDialog commitDialog = getCommitDialog(shell, indexDiff, preselectedFiles, commitMessage);
return doCommit(commitDialog);
}
private IndexDiff buildIndexHeadDiffList(Repository repo, IProgressMonitor monitor)
throws IOException, OperationCanceledException {
monitor.beginTask(Messages.MONITOR_CALCULATING_DIFF, 1000);
try {
WorkingTreeIterator it = IteratorService.createInitialIterator(repo);
if (it == null) {
throw new OperationCanceledException(); // workspace is closed
}
IndexDiff indexDiff = new IndexDiff(repo, org.eclipse.jgit.lib.Constants.HEAD, it);
indexDiff.diff();
Set<String> indexChanges = Sets.newHashSet();
Set<String> notIndexed = Sets.newHashSet();
notTracked = Sets.newHashSet();
includeList(indexDiff.getAdded(), indexChanges);
includeList(indexDiff.getChanged(), indexChanges);
includeList(indexDiff.getRemoved(), indexChanges);
includeList(indexDiff.getMissing(), notIndexed);
includeList(indexDiff.getModified(), notIndexed);
includeList(indexDiff.getUntracked(), notTracked);
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
return indexDiff;
} finally {
monitor.done();
}
}
private void includeList(Set<String> added, Set<String> category) {
for (String filename : added) {
if (!files.contains(filename)) {
files.add(filename);
}
category.add(filename);
}
}
private void getPreselectedFiles(IResource[] selectedResources, final Set<String> preselectedFiles) {
for (String fileName : files) {
URI uri = new File(delegate.getGitRepo().getWorkTree(), fileName).toURI();
for (IResource resource : selectedResources) {
if (resource.getFullPath().toFile().equals(new File(uri))) {
preselectedFiles.add(fileName);
}
}
}
}
private Collection<ISnippet> getChangedSnippets(Collection<ISnippet> snippets, final Set<String> preselectedFiles) {
return Collections2.filter(snippets, new Predicate<ISnippet>() {
@Override
public boolean apply(ISnippet input) {
UUID uuid = input.getUuid();
for (String preselectedFile : preselectedFiles) {
if (preselectedFile
.substring(SNIPPETS_DIR.length(), preselectedFile.length() - EXT_JSON.length() - 1)
.equals(uuid.toString())) {
return true;
}
}
return false;
}
});
}
private String getCommitMessage(Collection<ISnippet> snippets) {
StringBuilder sb = new StringBuilder();
String header = ""; //$NON-NLS-1$
if (snippets.size() == 1) {
ISnippet snippet = snippets.iterator().next();
header = format("Snippet contribution: {0} - {1}", snippet.getName(), snippet.getDescription());
} else {
header = format("This contributes {0} snippets", snippets.size());
}
sb.append(abbreviate(header, COMMIT_MESSAGE_FIRST_LINE_LENGTH));
sb.append(LINE_SEPARATOR);
sb.append(LINE_SEPARATOR);
for (ISnippet snippet : snippets) {
String snippetDescription = snippet.getDescription();
String line = ""; //$NON-NLS-1$
if (StringUtils.isEmptyOrNull(snippetDescription)) {
line = " * " + snippet.getName(); //$NON-NLS-1$
} else {
line = " * " + snippet.getName() + " - " + snippet.getDescription(); //$NON-NLS-1$ //$NON-NLS-2$
}
sb.append(abbreviate(line, COMMIT_MESSAGE_LINE_LENGTH));
sb.append(LINE_SEPARATOR);
}
return sb.toString();
}
private CommitDialog getCommitDialog(Shell shell, IndexDiff indexDiff, final Set<String> preselectedFiles,
String commitMessage) {
CommitHelper commitHelper = new CommitHelper(delegate.getGitRepo());
CommitDialog commitDialog = new CommitDialog(shell);
commitDialog.setAmendAllowed(false);
commitDialog.setFiles(delegate.getGitRepo(), files, indexDiff);
commitDialog.setPreselectedFiles(preselectedFiles);
commitDialog.setAuthor(commitHelper.getAuthor());
commitDialog.setCommitter(commitHelper.getCommitter());
commitDialog.setCommitMessage(commitMessage);
return commitDialog;
}
private boolean doCommit(CommitDialog commitDialog) {
/*
* TODO This is a workaround for CommitDialog shortcomings.
*
* @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=447236
*/
IPreferenceStore preferenceStore = org.eclipse.egit.ui.Activator.getDefault().getPreferenceStore();
boolean includeUntrackedPreference = preferenceStore.getBoolean(UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED);
try {
preferenceStore.setValue(UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED, true);
if (commitDialog.open() != IDialogConstants.OK_ID) {
return false;
}
} finally {
preferenceStore.setValue(UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED, includeUntrackedPreference);
}
final CommitOperation commitOperation;
try {
commitOperation = new CommitOperation(delegate.getGitRepo(), commitDialog.getSelectedFiles(), notTracked,
commitDialog.getAuthor(), commitDialog.getCommitter(), commitDialog.getCommitMessage());
} catch (CoreException e) {
Activator.handleError(Messages.ERROR_COMMIT_FAILED, e, true);
return false;
}
commitOperation.setComputeChangeId(commitDialog.getCreateChangeId());
commitOperation.setCommitAll(false);
Job commitJob = new CommitJob(delegate.getGitRepo(), commitOperation);
commitJob.setRule(schedulingRule);
commitJob.schedule();
return true;
}
private void push(Repository repository, final Shell shell) {
RemoteConfig config = SimpleConfigurePushDialog.getConfiguredRemote(repository);
int timeout = Activator.getDefault().getPreferenceStore().getInt(UIPreferences.REMOTE_CONNECTION_TIMEOUT);
final PushOperation push = new PushOperation(repository, config.getName(), false, timeout);
push.setCredentialsProvider(GitSnippetRepository.getCredentialsProvider(delegate.getPushUrl()));
Job pushJob = new Job(Messages.JOB_NAME_PUSHING_SNIPPETS_TO_REMOTE_GIT_REPO) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
push.run(monitor);
PushOperationResult operationResult = push.getOperationResult();
for (URIish uri : operationResult.getURIs()) {
String errorMessage = operationResult.getErrorMessage(uri);
if (errorMessage == null) {
return Status.OK_STATUS;
} else {
return new Status(IStatus.ERROR, Constants.BUNDLE_ID, MessageFormat
.format(Messages.ERROR_FAILURE_TO_PUSH_SNIPPETS_TO_REMOTE_GIT_REPO, errorMessage));
}
}
return Status.OK_STATUS;
} catch (InvocationTargetException e) {
return new Status(IStatus.ERROR, Constants.BUNDLE_ID,
Messages.ERROR_EXCEPTION_WHILE_PUSHING_SNIPPETS_TO_REMOTE_GIT_REPO, e);
}
}
};
pushJob.setRule(schedulingRule);
pushJob.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(final IJobChangeEvent event) {
shell.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (event.getResult().isOK()) {
MessageDialog.openInformation(shell, Messages.DIALOG_TITLE_GIT_PUSH_SUCCESSFUL,
Messages.DIALOG_MESSAGE_GIT_PUSH_SUCCESSFUL);
}
}
});
}
});
pushJob.schedule();
}
private void reset(final Workspace workspace) {
ResetOperation reset = new ResetOperation(delegate.getGitRepo(), "origin/" + Snippet.FORMAT_VERSION, //$NON-NLS-1$
ResetType.MIXED) {
@Override
public ISchedulingRule getSchedulingRule() {
return schedulingRule;
}
};
JobUtil.scheduleUserJob(reset, Messages.JOB_RESETTING_GIT_REPOSITORY, null);
}
}