blob: 9743cad9536197133eee35a2d00ec46f99e11e16 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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 com.google.idea.blaze.base.vcs.git;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.idea.blaze.base.async.process.ExternalTask;
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.google.idea.blaze.base.sync.workspace.WorkingSet;
import com.google.idea.blaze.base.vcs.BlazeVcsHandler;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import javax.annotation.Nullable;
/** Vcs diff provider for git */
public class GitBlazeVcsHandler implements BlazeVcsHandler {
private static final Logger logger = Logger.getInstance(GitBlazeVcsHandler.class);
@Override
public String getVcsName() {
return "git";
}
@Override
public boolean handlesProject(BuildSystem buildSystem, WorkspaceRoot workspaceRoot) {
return buildSystem == BuildSystem.Bazel
&& isGitRepository(workspaceRoot)
&& tracksRemote(workspaceRoot);
}
@Override
public ListenableFuture<WorkingSet> getWorkingSet(
Project project,
BlazeContext context,
WorkspaceRoot workspaceRoot,
ListeningExecutorService executor) {
return executor.submit(
() -> {
String upstreamSha = getUpstreamSha(workspaceRoot, false);
if (upstreamSha == null) {
return null;
}
return GitWorkingSetProvider.calculateWorkingSet(workspaceRoot, upstreamSha, context);
});
}
@Nullable
@Override
public BlazeVcsSyncHandler createSyncHandler(Project project, WorkspaceRoot workspaceRoot) {
return null;
}
@Override
public ListenableFuture<String> getUpstreamContent(
Project project,
BlazeContext context,
WorkspaceRoot workspaceRoot,
WorkspacePath path,
ListeningExecutorService executor) {
return executor.submit(() -> getGitUpstreamContent(workspaceRoot, path));
}
private static String getGitUpstreamContent(WorkspaceRoot workspaceRoot, WorkspacePath path) {
String upstreamSha = getUpstreamSha(workspaceRoot, false);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ExternalTask.builder(workspaceRoot)
.args(
"git",
"show",
// Normally "For plain blobs, it shows the plain contents.", but let's add some
// options to be a bit more paranoid.
"--no-color",
"--no-expand-tabs",
"--no-notes",
"--no-textconv",
String.format("%s:./%s", upstreamSha, path.relativePath()))
.stdout(outputStream)
.build()
.run();
return outputStream.toString();
}
private static boolean isGitRepository(WorkspaceRoot workspaceRoot) {
// TODO: What if the git repo root is a parent directory of the workspace root?
// Just call 'git rev-parse --is-inside-work-tree' or similar instead?
File gitDir = new File(workspaceRoot.directory(), ".git");
return FileAttributeProvider.getInstance().isDirectory(gitDir);
}
/**
* If we're not on a git branch which tracks a remote, we have no way of determining a WorkingSet.
*/
private static boolean tracksRemote(WorkspaceRoot workspaceRoot) {
return getUpstreamSha(workspaceRoot, true) != null;
}
/**
* Returns the git commit SHA corresponding to the most recent commit in the current branch which
* matches a commit in the currently-tracked remote branch.
*/
@Nullable
public static String getUpstreamSha(WorkspaceRoot workspaceRoot, boolean suppressErrors) {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
int retVal =
ExternalTask.builder(workspaceRoot)
.args("git", "rev-parse", "@{u}")
.stdout(stdout)
.stderr(stderr)
.build()
.run();
if (retVal != 0) {
if (!suppressErrors) {
logger.error(stderr);
}
return null;
}
return StringUtil.trimEnd(stdout.toString(), "\n");
}
}