blob: 322e91f96cc82491168957da3eaa0e4875cf190f [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.collect.ImmutableList;
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.WorkspaceRoot;
import com.google.idea.blaze.base.settings.Blaze;
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 javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.File;
/**
* Vcs diff provider for git
*/
public class GitBlazeVcsHandler implements BlazeVcsHandler {
private static final Logger LOG = Logger.getInstance(GitBlazeVcsHandler.class);
@Nullable
@Override
public String getClientName(WorkspaceRoot workspaceRoot) {
return null;
}
@Override
public boolean handlesProject(Project project, WorkspaceRoot workspaceRoot) {
return Blaze.getBuildSystem(project) == BuildSystem.Bazel
&& isGitRepository(workspaceRoot)
&& tracksRemote(workspaceRoot);
}
@Override
public ListenableFuture<WorkingSet> getWorkingSet(Project project,
WorkspaceRoot workspaceRoot,
ListeningExecutorService executor) {
return executor.submit(() -> {
String upstreamSha = getUpstreamSha(workspaceRoot, false);
if (upstreamSha == null) {
return null;
}
return GitDiffProvider.calculateDiff(workspaceRoot, upstreamSha);
});
}
@Nullable
@Override
public BlazeVcsSyncHandler createSyncHandler(Project project,
WorkspaceRoot workspaceRoot) {
return null;
}
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) {
return getConsoleOutput(workspaceRoot, ImmutableList.of("git", "rev-parse", "@{u}"), suppressErrors);
}
/**
* @return the console output, in string form, or null if there was a non-zero exit code.
*/
@Nullable
private static String getConsoleOutput(WorkspaceRoot workspaceRoot, ImmutableList<String> command, boolean suppressErrors) {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
int retVal = ExternalTask.builder(workspaceRoot, command)
.stdout(stdout)
.stderr(stderr)
.build()
.run();
if (retVal != 0) {
if (!suppressErrors) {
LOG.error(stderr);
}
return null;
}
return StringUtil.trimEnd(stdout.toString(), "\n");
}
}