blob: bda1b82a1340baa6fdac57993a11ea42360cd077 [file] [log] [blame]
// Copyright 2008-2010 Victor Iacoban
//
// 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 org.zmlx.hg4idea.command;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsFileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.HgChange;
import org.zmlx.hg4idea.HgFile;
import org.zmlx.hg4idea.HgFileStatusEnum;
import org.zmlx.hg4idea.HgRevisionNumber;
import org.zmlx.hg4idea.execution.HgCommandExecutor;
import org.zmlx.hg4idea.execution.HgCommandResult;
import java.io.File;
import java.util.*;
public class HgStatusCommand {
private static final Logger LOG = Logger.getInstance(HgStatusCommand.class.getName());
private static final int ITEM_COUNT = 3;
private static final int STATUS_INDEX = 0;
@NotNull private final Project myProject;
private final boolean myIncludeAdded;
private final boolean myIncludeModified;
private final boolean myIncludeRemoved;
private final boolean myIncludeDeleted;
private final boolean myIncludeUnknown;
private final boolean myIncludeIgnored;
private final boolean myIncludeCopySource;
@Nullable private final HgRevisionNumber myBaseRevision;
@Nullable private final HgRevisionNumber myTargetRevision;
public static class Builder {
private boolean includeAdded;
private boolean includeModified;
private boolean includeRemoved;
private boolean includeDeleted;
private boolean includeUnknown;
private boolean includeIgnored;
private boolean includeCopySource;
private HgRevisionNumber baseRevision;
private HgRevisionNumber targetRevision;
public Builder(boolean initValue) {
includeAdded = initValue;
includeModified = initValue;
includeRemoved = initValue;
includeDeleted = initValue;
includeUnknown = initValue;
includeIgnored = initValue;
includeCopySource = initValue;
baseRevision = null;
targetRevision = null;
}
public Builder unknown(boolean val) {
includeUnknown = val;
return this;
}
public Builder ignored(boolean val) {
includeIgnored = val;
return this;
}
public Builder copySource(boolean val) {
includeCopySource = val;
return this;
}
public Builder baseRevision(HgRevisionNumber val) {
baseRevision = val;
return this;
}
public Builder targetRevision(HgRevisionNumber val) {
targetRevision = val;
return this;
}
public HgStatusCommand build(@NotNull Project project) {
return new HgStatusCommand(project, this);
}
}
private HgStatusCommand(@NotNull Project project, @NotNull Builder builder) {
myProject = project;
myIncludeAdded = builder.includeAdded;
myIncludeModified = builder.includeModified;
myIncludeRemoved = builder.includeRemoved;
myIncludeDeleted = builder.includeDeleted;
myIncludeUnknown = builder.includeUnknown;
myIncludeIgnored = builder.includeIgnored;
myIncludeCopySource = builder.includeCopySource;
myBaseRevision = builder.baseRevision;
myTargetRevision = builder.targetRevision;
}
public Set<HgChange> execute(VirtualFile repo) {
return execute(repo, null);
}
public Set<HgChange> execute(VirtualFile repo, @Nullable Collection<FilePath> paths) {
if (repo == null) {
return Collections.emptySet();
}
HgCommandExecutor executor = new HgCommandExecutor(myProject);
executor.setSilent(true);
List<String> options = new LinkedList<String>();
if (myIncludeAdded) {
options.add("--added");
}
if (myIncludeModified) {
options.add("--modified");
}
if (myIncludeRemoved) {
options.add("--removed");
}
if (myIncludeDeleted) {
options.add("--deleted");
}
if (myIncludeUnknown) {
options.add("--unknown");
}
if (myIncludeIgnored) {
options.add("--ignored");
}
if (myIncludeCopySource) {
options.add("--copies");
}
if (myBaseRevision != null && (!myBaseRevision.getRevision().isEmpty() || !myBaseRevision.getChangeset().isEmpty())) {
options.add("--rev");
options.add(StringUtil.isEmptyOrSpaces(myBaseRevision.getChangeset()) ? myBaseRevision.getRevision() : myBaseRevision.getChangeset());
if (myTargetRevision != null) {
options.add("--rev");
options.add(myTargetRevision.getChangeset());
}
}
final Set<HgChange> changes = new HashSet<HgChange>();
if (paths != null) {
final List<List<String>> chunked = VcsFileUtil.chunkPaths(repo, paths);
for (List<String> chunk : chunked) {
List<String> args = new ArrayList<String>();
args.addAll(options);
args.addAll(chunk);
HgCommandResult result = executor.executeInCurrentThread(repo, "status", args);
changes.addAll(parseChangesFromResult(repo, result, args));
}
} else {
HgCommandResult result = executor.executeInCurrentThread(repo, "status", options);
changes.addAll(parseChangesFromResult(repo, result, options));
}
return changes;
}
private Collection<HgChange> parseChangesFromResult(VirtualFile repo, HgCommandResult result, List<String> args) {
final Set<HgChange> changes = new HashSet<HgChange>();
HgChange previous = null;
if (result == null) {
return changes;
}
List<String> errors = result.getErrorLines();
if (errors != null && !errors.isEmpty()) {
if (result.getExitValue() != 0) {
String title = "Could not execute hg status command ";
LOG.warn(title + errors.toString());
VcsNotifier.getInstance(myProject).logInfo(title, errors.toString());
return changes;
}
LOG.warn(errors.toString());
}
for (String line : result.getOutputLines()) {
if (StringUtil.isEmptyOrSpaces(line) || line.length() < ITEM_COUNT) {
LOG.warn("Unexpected line in status '" + line + '\'');
continue;
}
char statusChar = line.charAt(STATUS_INDEX);
HgFileStatusEnum status = HgFileStatusEnum.parse(statusChar);
if (status == null) {
LOG.error("Unknown status [" + statusChar + "] in line [" + line + "]" + "\n with arguments " + args);
continue;
}
File ioFile = new File(repo.getPath(), line.substring(2));
if (HgFileStatusEnum.COPY == status && previous != null
&& previous.getStatus() == HgFileStatusEnum.ADDED) {
previous.setStatus(HgFileStatusEnum.COPY);
previous.setBeforeFile(new HgFile(repo, ioFile));
previous = null;
} else {
previous = new HgChange(new HgFile(repo, ioFile), status);
changes.add(previous);
}
}
return changes;
}
@NotNull
public Collection<VirtualFile> getHgUntrackedFiles(@NotNull VirtualFile repo, @NotNull List<VirtualFile> files) throws VcsException {
Collection<VirtualFile> untrackedFiles = new HashSet<VirtualFile>();
List<FilePath> filePaths = ObjectsConvertor.vf2fp(files);
Set<HgChange> change = execute(repo, filePaths);
for (HgChange hgChange : change) {
untrackedFiles.add(hgChange.afterFile().toFilePath().getVirtualFile());
}
return untrackedFiles;
}
}