[git] Refactor GitRepositoryReader

* Remove the complicated PackedRefsLineResultHandler.
* Instead make parseRefsLine return a (hash, name) pair,
  and supply a nullable Condition to split cases when only the first
  match is needed, and when all values are needed.
* Avoid the substring memory issue while parsing line.
This commit is contained in:
Kirill Likhodedov
2014-04-15 19:58:07 +04:00
parent 06a2de76a7
commit bf5c358fa1
@@ -19,8 +19,10 @@ import com.intellij.dvcs.repo.RepoStateException;
import com.intellij.dvcs.repo.Repository;
import com.intellij.dvcs.repo.RepositoryUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.impl.HashImpl;
import git4idea.GitBranch;
@@ -37,7 +39,6 @@ import java.io.File;
import java.io.FileReader;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -214,38 +215,40 @@ class GitRepositoryReader {
return null;
}
final AtomicReference<String> hashRef = new AtomicReference<String>();
readPackedRefsFile(new PackedRefsLineResultHandler() {
List<HashAndName> hashAndNames = readPackedRefsFile(new Condition<HashAndName>() {
@Override
public void handleResult(String hash, String branchName) {
if (hash == null || branchName == null) {
return;
}
if (branchName.endsWith(ref)) {
hashRef.set(shortBuffer(hash));
stop();
}
public boolean value(HashAndName hashAndName) {
return hashAndName.name.endsWith(ref);
}
});
if (hashRef.get() != null) {
return hashRef.get();
}
return null;
return hashAndNames.get(0).hash;
}
private void readPackedRefsFile(@NotNull final PackedRefsLineResultHandler handler) {
RepositoryUtil.tryOrThrow(new Callable<String>() {
/**
* @param firstMatchCondition If specified, we read the packed-refs file until the first entry which matches the given condition,
* and return a singleton list of this entry.
* If null, the whole file is read, and all valid entries are returned.
*/
private List<HashAndName> readPackedRefsFile(@Nullable final Condition<HashAndName> firstMatchCondition) {
return RepositoryUtil.tryOrThrow(new Callable<List<HashAndName>>() {
@Override
public String call() throws Exception {
public List<HashAndName> call() throws Exception {
List<HashAndName> hashAndNames = ContainerUtil.newArrayList();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(myPackedRefsFile));
String line;
while ((line = reader.readLine()) != null) {
parsePackedRefsLine(line, handler);
if (handler.stopped()) {
return null;
for (String line = reader.readLine(); line != null ; line = reader.readLine()) {
HashAndName hashAndName = parsePackedRefsLine(line);
if (hashAndName == null) {
continue;
}
if (firstMatchCondition != null) {
if (firstMatchCondition.value(hashAndName)) {
return Collections.singletonList(hashAndName);
}
}
else {
hashAndNames.add(hashAndName);
}
}
}
@@ -254,7 +257,7 @@ class GitRepositoryReader {
reader.close();
}
}
return null;
return hashAndNames;
}
}, myPackedRefsFile);
}
@@ -370,27 +373,24 @@ class GitRepositoryReader {
return GitBranchesCollection.EMPTY;
}
readPackedRefsFile(new PackedRefsLineResultHandler() {
@Override public void handleResult(@Nullable String hashString, @Nullable String branchName) {
if (hashString == null || branchName == null) {
return;
}
hashString = shortBuffer(hashString);
Hash hash = createHash(hashString);
if (hash == null) {
return;
}
if (branchName.startsWith(REFS_HEADS_PREFIX)) {
localBranches.add(new GitLocalBranch(branchName, hash));
}
else if (branchName.startsWith(REFS_REMOTES_PREFIX)) {
GitRemoteBranch remoteBranch = GitBranchUtil.parseRemoteBranch(branchName, hash, remotes);
if (remoteBranch != null) {
remoteBranches.add(remoteBranch);
}
List<HashAndName> hashAndNames = readPackedRefsFile(null);
for (HashAndName hashAndName : hashAndNames) {
Hash hash = createHash(hashAndName.hash);
if (hash == null) {
continue;
}
String branchName = hashAndName.name;
if (branchName.startsWith(REFS_HEADS_PREFIX)) {
localBranches.add(new GitLocalBranch(branchName, hash));
}
else if (branchName.startsWith(REFS_REMOTES_PREFIX)) {
GitRemoteBranch remoteBranch = GitBranchUtil.parseRemoteBranch(branchName, hash, remotes);
if (remoteBranch != null) {
remoteBranches.add(remoteBranch);
}
}
});
}
return new GitBranchesCollection(localBranches, remoteBranches);
}
@@ -419,53 +419,49 @@ class GitRepositoryReader {
}
/**
* Parses a line from the .git/packed-refs file.
* Passes the parsed hash-branch pair to the resultHandler.
* Comments, tags and incorrectly formatted lines are ignored, and (null, null) is passed to the handler then.
* Using a special handler may seem to be an overhead, but it is to avoid code duplication in two methods that parse packed-refs.
* Parses a line from the .git/packed-refs file returning a pair of hash and ref name.
* Comments and tags are ignored, and null is returned.
* Incorrectly formatted lines are ignored, a warning is printed to the log, null is returned.
* A line indicating a hash which an annotated tag (specified in the previous line) points to, is ignored: null is returned.
*/
private static void parsePackedRefsLine(@NotNull String line, @NotNull PackedRefsLineResultHandler resultHandler) {
try {
line = line.trim();
char firstChar = line.isEmpty() ? 0 : line.charAt(0);
if (firstChar == '#') { // ignoring comments
return;
@Nullable
private static HashAndName parsePackedRefsLine(@NotNull String line) {
line = line.trim();
char firstChar = line.isEmpty() ? 0 : line.charAt(0);
if (firstChar == '#') { // ignoring comments
return null;
}
if (firstChar == '^') {
// ignoring the hash which an annotated tag above points to
return null;
}
String hash = null;
int i;
for (i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (!Character.isLetterOrDigit(c)) {
hash = line.substring(0, i);
break;
}
if (firstChar == '^') {
// ignoring the hash which an annotated tag above points to
return;
}
String hash = null;
int i;
for (i = 0; i < line.length(); i++) {
}
String branch = null;
int start = i;
if (hash != null && start < line.length() && line.charAt(start++) == ' ') {
for (i = start; i < line.length(); i++) {
char c = line.charAt(i);
if (!Character.isLetterOrDigit(c)) {
hash = line.substring(0, i);
if (Character.isWhitespace(c)) {
break;
}
}
String branch = null;
int start = i;
if (hash != null && start < line.length() && line.charAt(start++) == ' ') {
for (i = start; i < line.length(); i++) {
char c = line.charAt(i);
if (Character.isWhitespace(c)) {
break;
}
}
branch = line.substring(start, i);
}
branch = line.substring(start, i);
}
if (hash != null && branch != null) {
resultHandler.handleResult(hash.trim(), branch);
}
else {
LOG.info("Ignoring invalid packed-refs line: [" + line + "]");
}
}
finally {
resultHandler.handleResult(null, null);
if (hash != null && branch != null) {
return new HashAndName(shortBuffer(hash.trim()), shortBuffer(branch));
}
LOG.info("Ignoring invalid packed-refs line: [" + line + "]");
return null;
}
@NotNull
@@ -473,20 +469,13 @@ class GitRepositoryReader {
return new String(raw);
}
private abstract static class PackedRefsLineResultHandler {
private boolean myStopped;
private static class HashAndName {
private final String hash;
private final String name;
abstract void handleResult(@Nullable String hash, @Nullable String branchName);
/**
* Call this to stop further lines reading.
*/
final void stop() {
myStopped = true;
}
final boolean stopped() {
return myStopped;
public HashAndName(String hash, String name) {
this.hash = hash;
this.name = name;
}
}