blob: a9857963124fb56f3537969534baf1dda9a54ca0 [file] [log] [blame]
/*
* Copyright 2000-2010 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 git4idea.history;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.GitUtil;
import git4idea.commands.GitHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static git4idea.history.GitLogParser.GitLogOption.*;
/**
* One record (commit information) returned by git log output.
* The access methods try heavily to return some default value if real is unavailable, for example, blank string is better than null.
* BUT if one tries to get an option which was not specified to the GitLogParser, one will get null.
* @see git4idea.history.GitLogParser
*/
class GitLogRecord {
private static final Logger LOG = Logger.getInstance(GitLogRecord.class);
private final Map<GitLogParser.GitLogOption, String> myOptions;
private final List<String> myPaths;
private final List<GitLogStatusInfo> myStatusInfo;
private final boolean mySupportsRawBody;
private GitHandler myHandler;
GitLogRecord(@NotNull Map<GitLogParser.GitLogOption, String> options, @NotNull List<String> paths, @NotNull List<GitLogStatusInfo> statusInfo, boolean supportsRawBody) {
myOptions = options;
myPaths = paths;
myStatusInfo = statusInfo;
mySupportsRawBody = supportsRawBody;
}
private List<String> getPaths() {
return myPaths;
}
@NotNull
List<GitLogStatusInfo> getStatusInfos() {
return myStatusInfo;
}
@NotNull
public List<FilePath> getFilePaths(VirtualFile root) throws VcsException {
List<FilePath> res = new ArrayList<FilePath>();
String prefix = root.getPath() + "/";
for (String strPath : getPaths()) {
final String subPath = GitUtil.unescapePath(strPath);
final FilePath revisionPath = VcsUtil.getFilePathForDeletedFile(prefix + subPath, false);
res.add(revisionPath);
}
return res;
}
private String lookup(GitLogParser.GitLogOption key) {
return shortBuffer(myOptions.get(key));
}
// trivial access methods
String getHash() { return lookup(HASH); }
String getAuthorName() { return lookup(AUTHOR_NAME); }
String getAuthorEmail() { return lookup(AUTHOR_EMAIL); }
String getCommitterName() { return lookup(COMMITTER_NAME); }
String getCommitterEmail() { return lookup(COMMITTER_EMAIL); }
String getSubject() { return lookup(SUBJECT); }
String getBody() { return lookup(BODY); }
String getRawBody() { return lookup(RAW_BODY); }
String getShortenedRefLog() { return lookup(SHORT_REF_LOG_SELECTOR); }
// access methods with some formatting or conversion
@NotNull
Date getDate() {
return new Date(getCommitTime());
}
long getCommitTime() {
try {
return Long.parseLong(myOptions.get(COMMIT_TIME).trim()) * 1000;
}
catch (NumberFormatException e) {
LOG.error("Couldn't get commit time from " + toString() + ", while executing " + myHandler, e);
return 0;
}
}
long getAuthorTimeStamp() {
try {
return Long.parseLong(myOptions.get(AUTHOR_TIME).trim()) * 1000;
}
catch (NumberFormatException e) {
LOG.error("Couldn't get author time from " + toString() + ", while executing " + myHandler, e);
return 0;
}
}
String getFullMessage() {
return mySupportsRawBody ? getRawBody().trim() : ((getSubject() + "\n\n" + getBody()).trim());
}
String[] getParentsHashes() {
final String parents = lookup(PARENTS);
if (parents.trim().length() == 0) return ArrayUtil.EMPTY_STRING_ARRAY;
return parents.split(" ");
}
public Collection<String> getRefs() {
final String decorate = myOptions.get(REF_NAMES);
return parseRefNames(decorate);
}
/**
* Returns the list of tags and the list of branches.
* A single method is used to return both, because they are returned together by Git and we don't want to parse them twice.
* @return
* @param allBranchesSet
*/
/*Pair<List<String>, List<String>> getTagsAndBranches(SymbolicRefs refs) {
final String decorate = myOptions.get(REF_NAMES);
final String[] refNames = parseRefNames(decorate);
final List<String> tags = refNames.length > 0 ? new ArrayList<String>() : Collections.<String>emptyList();
final List<String> branches = refNames.length > 0 ? new ArrayList<String>() : Collections.<String>emptyList();
for (String refName : refNames) {
if (refs.contains(refName)) {
// also some gits can return ref name twice (like (HEAD, HEAD), so check we will show it only once)
if (!branches.contains(refName)) {
branches.add(shortBuffer(refName));
}
} else {
if (!tags.contains(refName)) {
tags.add(shortBuffer(refName));
}
}
}
return Pair.create(tags, branches);
}*/
@NotNull
private static List<String> parseRefNames(@Nullable final String decoration) {
if (decoration == null) {
return ContainerUtil.emptyList();
}
final int startParentheses = decoration.indexOf("(");
final int endParentheses = decoration.indexOf(")");
if ((startParentheses == -1) || (endParentheses == -1)) return Collections.emptyList();
String refs = decoration.substring(startParentheses + 1, endParentheses);
String[] names = refs.split(", ");
return ContainerUtil.map(names, new Function<String, String>() {
@Override
public String fun(String item) {
int colon = item.indexOf(':');
return shortBuffer(colon > 0 ? item.substring(colon + 1).trim() : item);
}
});
}
private static String shortBuffer(String raw) {
return new String(raw);
}
public List<Change> parseChanges(Project project, VirtualFile vcsRoot) throws VcsException {
return GitChangesParser.parse(project, vcsRoot, myStatusInfo, getHash(), getDate(), Arrays.asList(getParentsHashes()));
}
/**
* for debugging purposes - see {@link GitUtil#parseTimestampWithNFEReport(String, git4idea.commands.GitHandler, String)}.
*/
public void setUsedHandler(GitHandler handler) {
myHandler = handler;
}
@Override
public String toString() {
return String.format("GitLogRecord{myOptions=%s, myPaths=%s, myStatusInfo=%s, mySupportsRawBody=%s, myHandler=%s}",
myOptions, myPaths, myStatusInfo, mySupportsRawBody, myHandler);
}
}