/* * Copyright 2000-2014 JetBrains s.r.o. * * 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.log; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Couple; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.CollectConsumer; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; import com.intellij.vcs.log.impl.LogDataImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgNameWithHashInfo; import org.zmlx.hg4idea.HgUpdater; import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.repo.HgConfig; import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgUtil; import java.text.SimpleDateFormat; import java.util.*; import static org.zmlx.hg4idea.util.HgUtil.HEAD_REFERENCE; import static org.zmlx.hg4idea.util.HgUtil.TIP_REFERENCE; public class HgLogProvider implements VcsLogProvider { private static final Logger LOG = Logger.getInstance(HgLogProvider.class); @NotNull private final Project myProject; @NotNull private final HgRepositoryManager myRepositoryManager; @NotNull private final VcsLogRefManager myRefSorter; @NotNull private final VcsLogObjectsFactory myVcsObjectsFactory; public HgLogProvider(@NotNull Project project, @NotNull HgRepositoryManager repositoryManager, @NotNull VcsLogObjectsFactory factory) { myProject = project; myRepositoryManager = repositoryManager; myRefSorter = new HgRefManager(); myVcsObjectsFactory = factory; } @NotNull @Override public DetailedLogData readFirstBlock(@NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException { List commits = HgHistoryUtil.loadMetadata(myProject, root, requirements.getCommitCount(), Collections.emptyList()); return new LogDataImpl(readAllRefs(root), commits); } @Override @NotNull public LogData readAllHashes(@NotNull VirtualFile root, @NotNull final Consumer commitConsumer) throws VcsException { Set userRegistry = ContainerUtil.newHashSet(); List commits = HgHistoryUtil.readAllHashes(myProject, root, new CollectConsumer(userRegistry), Collections.emptyList()); for (TimedVcsCommit commit : commits) { commitConsumer.consume(commit); } return new LogDataImpl(readAllRefs(root), userRegistry); } @NotNull @Override public List readShortDetails(@NotNull VirtualFile root, @NotNull List hashes) throws VcsException { return HgHistoryUtil.readMiniDetails(myProject, root, hashes); } @NotNull @Override public List readFullDetails(@NotNull VirtualFile root, @NotNull List hashes) throws VcsException { return HgHistoryUtil.history(myProject, root, -1, HgHistoryUtil.prepareHashes(hashes)); } @NotNull private Set readAllRefs(@NotNull VirtualFile root) throws VcsException { myRepositoryManager.waitUntilInitialized(); if (myProject.isDisposed()) { return Collections.emptySet(); } HgRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { LOG.error("Repository not found for root " + root); return Collections.emptySet(); } repository.update(); Map> branches = repository.getBranches(); Set openedBranchNames = repository.getOpenedBranches(); Collection bookmarks = repository.getBookmarks(); Collection tags = repository.getTags(); Collection localTags = repository.getLocalTags(); Set refs = new HashSet(branches.size() + bookmarks.size()); for (Map.Entry> entry : branches.entrySet()) { String branchName = entry.getKey(); boolean opened = openedBranchNames.contains(branchName); for (Hash hash : entry.getValue()) { refs.add(myVcsObjectsFactory.createRef(hash, branchName, opened ? HgRefManager.BRANCH : HgRefManager.CLOSED_BRANCH, root)); } } for (HgNameWithHashInfo bookmarkInfo : bookmarks) { refs.add(myVcsObjectsFactory.createRef(bookmarkInfo.getHash(), bookmarkInfo.getName(), HgRefManager.BOOKMARK, root)); } String currentRevision = repository.getCurrentRevision(); if (currentRevision != null) { // null => fresh repository refs.add(myVcsObjectsFactory.createRef(myVcsObjectsFactory.createHash(currentRevision), HEAD_REFERENCE, HgRefManager.HEAD, root)); } String tipRevision = repository.getTipRevision(); if (tipRevision != null) { // null => fresh repository refs.add(myVcsObjectsFactory.createRef(myVcsObjectsFactory.createHash(tipRevision), TIP_REFERENCE, HgRefManager.TIP, root)); } for (HgNameWithHashInfo tagInfo : tags) { refs.add(myVcsObjectsFactory.createRef(tagInfo.getHash(), tagInfo.getName(), HgRefManager.TAG, root)); } for (HgNameWithHashInfo localTagInfo : localTags) { refs.add(myVcsObjectsFactory.createRef(localTagInfo.getHash(), localTagInfo.getName(), HgRefManager.LOCAL_TAG, root)); } return refs; } @NotNull @Override public VcsKey getSupportedVcs() { return HgVcs.getKey(); } @NotNull @Override public VcsLogRefManager getReferenceManager() { return myRefSorter; } @Override public void subscribeToRootRefreshEvents(@NotNull final Collection roots, @NotNull final VcsLogRefresher refresher) { myProject.getMessageBus().connect(myProject).subscribe(HgVcs.STATUS_TOPIC, new HgUpdater() { @Override public void update(Project project, @Nullable VirtualFile root) { if (root != null && roots.contains(root)) { refresher.refresh(root); } } }); } @NotNull @Override public List getCommitsMatchingFilter(@NotNull final VirtualFile root, @NotNull VcsLogFilterCollection filterCollection, int maxCount) throws VcsException { List filterParameters = ContainerUtil.newArrayList(); // branch filter and user filter may be used several times without delimiter if (filterCollection.getBranchFilter() != null) { HgRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { LOG.error("Repository not found for root " + root); return Collections.emptyList(); } boolean atLeastOneBranchExists = false; for (String branchName : filterCollection.getBranchFilter().getBranchNames()) { if (branchName.equals(TIP_REFERENCE) || branchExists(repository, branchName)) { filterParameters.add(HgHistoryUtil.prepareParameter("branch", branchName)); atLeastOneBranchExists = true; } else if (branchName.equals(HEAD_REFERENCE)) { filterParameters.add(HgHistoryUtil.prepareParameter("branch", ".")); filterParameters.add("-r"); filterParameters.add("::."); //all ancestors for current revision; atLeastOneBranchExists = true; } } if (!atLeastOneBranchExists) { // no such branches => filter matches nothing return Collections.emptyList(); } } if (filterCollection.getUserFilter() != null) { for (String authorName : filterCollection.getUserFilter().getUserNames(root)) { filterParameters.add(HgHistoryUtil.prepareParameter("user", authorName)); } } if (filterCollection.getDateFilter() != null) { StringBuilder args = new StringBuilder(); final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm"); filterParameters.add("-d"); VcsLogDateFilter filter = filterCollection.getDateFilter(); if (filter.getAfter() != null) { if (filter.getBefore() != null) { args.append(dateFormatter.format(filter.getAfter())).append(" to ").append(dateFormatter.format(filter.getBefore())); } else { args.append('>').append(dateFormatter.format(filter.getAfter())); } } else if (filter.getBefore() != null) { args.append('<').append(dateFormatter.format(filter.getBefore())); } filterParameters.add(args.toString()); } if (filterCollection.getTextFilter() != null) { String textFilter = filterCollection.getTextFilter().getText(); filterParameters.add(HgHistoryUtil.prepareParameter("keyword", textFilter)); } if (filterCollection.getStructureFilter() != null) { for (VirtualFile file : filterCollection.getStructureFilter().getFiles(root)) { filterParameters.add(file.getPath()); } } return HgHistoryUtil.readAllHashes(myProject, root, Consumer.EMPTY_CONSUMER, filterParameters); } @Nullable @Override public VcsUser getCurrentUser(@NotNull VirtualFile root) throws VcsException { String userName = HgConfig.getInstance(myProject, root).getNamedConfig("ui", "username"); //order of variables to identify hg username see at mercurial/ui.py if (userName == null) { userName = System.getenv("HGUSER"); if (userName == null) { userName = System.getenv("USER"); if (userName == null) { userName = System.getenv("LOGNAME"); if (userName == null) { return null; } } } } Couple userArgs = HgUtil.parseUserNameAndEmail(userName); return myVcsObjectsFactory.createUser(userArgs.getFirst(), userArgs.getSecond()); } @NotNull @Override public Collection getContainingBranches(@NotNull VirtualFile root, @NotNull Hash commitHash) throws VcsException { return HgHistoryUtil.getDescendingHeadsOfBranches(myProject, root, commitHash); } private static boolean branchExists(@NotNull HgRepository repository, @NotNull String branchName) { return repository.getBranches().keySet().contains(branchName) || HgUtil.getNamesWithoutHashes(repository.getBookmarks()).contains(branchName); } }