| // 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; |
| } |
| } |