Skip to content

Commit

Permalink
Merge branch 'main' into 102-implement-update-commandlet
Browse files Browse the repository at this point in the history
  • Loading branch information
salimbouch authored Apr 7, 2024
2 parents 59f290a + ecc2e58 commit 16cf912
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 45 deletions.
58 changes: 21 additions & 37 deletions cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@
public class GitContextImpl implements GitContext {
private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000);

;

private final IdeContext context;

private ProcessContext processContext;
private final ProcessContext processContext;

private final ProcessMode PROCESS_MODE = ProcessMode.DEFAULT;

/**
* @param context the {@link IdeContext context}.
*/
public GitContextImpl(IdeContext context) {

this.context = context;

this.processContext = this.context.newProcess().executable("git").withEnvVar("GIT_TERMINAL_PROMPT", "0");
}

@Override
Expand Down Expand Up @@ -102,10 +102,9 @@ public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository)
throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!");
}

initializeProcessContext(targetRepository);
if (Files.isDirectory(targetRepository.resolve(".git"))) {
// checks for remotes
ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE);
ProcessResult result = this.processContext.addArg("remote").run(PROCESS_MODE);
List<String> remotes = result.getOut();
if (remotes.isEmpty()) {
String message = targetRepository
Expand All @@ -128,8 +127,8 @@ public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository)
* Handles errors which occurred during git pull.
*
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
* final folder that will contain the ".git" subfolder.
* @param result the {@link ProcessResult} to evaluate.
*/
private void handleErrors(Path targetRepository, ProcessResult result) {
Expand All @@ -142,8 +141,8 @@ private void handleErrors(Path targetRepository, ProcessResult result) {
} else {
this.context.error(message);
if (this.context.isOnline()) {
this.context.error(
"See above error for details. If you have local changes, please stash or revert and retry.");
this.context
.error("See above error for details. If you have local changes, please stash or revert and retry.");
} else {
this.context.error(
"It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline).");
Expand All @@ -153,26 +152,11 @@ private void handleErrors(Path targetRepository, ProcessResult result) {
}
}

/**
* Lazily initializes the {@link ProcessContext}.
*
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
*/
private void initializeProcessContext(Path targetRepository) {

if (this.processContext == null) {
this.processContext = this.context.newProcess().directory(targetRepository).executable("git")
.withEnvVar("GIT_TERMINAL_PROMPT", "0");
}
}

@Override
public void clone(GitUrl gitRepoUrl, Path targetRepository) {

URL parsedUrl = gitRepoUrl.parseUrl();
initializeProcessContext(targetRepository);
this.processContext.directory(targetRepository);
ProcessResult result;
if (!this.context.isOffline()) {
this.context.getFileAccess().mkdirs(targetRepository);
Expand All @@ -182,14 +166,14 @@ public void clone(GitUrl gitRepoUrl, Path targetRepository) {
this.processContext.addArg("-q");
}
this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", ".");
result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE);
result = this.processContext.run(PROCESS_MODE);
if (!result.isSuccessful()) {
this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository);
}
String branch = gitRepoUrl.branch();
if (branch != null) {
this.processContext.addArgs("checkout", branch);
result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE);
result = this.processContext.run(PROCESS_MODE);
if (!result.isSuccessful()) {
this.context.warning("Git failed to checkout to branch {}", branch);
}
Expand All @@ -202,10 +186,10 @@ public void clone(GitUrl gitRepoUrl, Path targetRepository) {
@Override
public void pull(Path targetRepository) {

initializeProcessContext(targetRepository);
this.processContext.directory(targetRepository);
ProcessResult result;
// pull from remote
result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE);
result = this.processContext.addArg("--no-pager").addArg("pull").run(PROCESS_MODE);

if (!result.isSuccessful()) {
Map<String, String> remoteAndBranchName = retrieveRemoteAndBranchName();
Expand All @@ -218,7 +202,7 @@ public void pull(Path targetRepository) {
private Map<String, String> retrieveRemoteAndBranchName() {

Map<String, String> remoteAndBranchName = new HashMap<>();
ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE);
ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(PROCESS_MODE);
List<String> remotes = remoteResult.getOut();
if (!remotes.isEmpty()) {
for (String remote : remotes) {
Expand Down Expand Up @@ -246,17 +230,17 @@ private Map<String, String> retrieveRemoteAndBranchName() {
@Override
public void reset(Path targetRepository, String remoteName, String branchName) {

initializeProcessContext(targetRepository);
this.processContext.directory(targetRepository);
ProcessResult result;
// check for changed files
result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE);
result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(PROCESS_MODE);

if (!result.isSuccessful()) {
// reset to origin/master
context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository,
remoteName, branchName);
result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName)
.run(ProcessMode.DEFAULT_CAPTURE);
.run(PROCESS_MODE);

if (!result.isSuccessful()) {
context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository);
Expand All @@ -268,16 +252,16 @@ public void reset(Path targetRepository, String remoteName, String branchName) {
@Override
public void cleanup(Path targetRepository) {

initializeProcessContext(targetRepository);
this.processContext.directory(targetRepository);
ProcessResult result;
// check for untracked files
result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard")
.run(ProcessMode.DEFAULT_CAPTURE);
.run(PROCESS_MODE);

if (!result.getOut().isEmpty()) {
// delete untracked files
context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository);
result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE);
result = this.processContext.addArg("clean").addArg("-df").run(PROCESS_MODE);

if (!result.isSuccessful()) {
context.warning("Git failed to clean the repository {}.", targetRepository);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import com.devonfw.tools.ide.cli.CliException;
Expand Down Expand Up @@ -134,18 +135,16 @@ public ProcessResult run(ProcessMode processMode) {

this.processBuilder.command(args);

Process process = this.processBuilder.start();

List<String> out = null;
List<String> err = null;

Process process = this.processBuilder.start();

if (processMode == ProcessMode.DEFAULT_CAPTURE) {
try (BufferedReader outReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
out = outReader.lines().collect(Collectors.toList());
}
try (BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
err = errReader.lines().collect(Collectors.toList());
}
CompletableFuture<List<String>> outFut = readInputStream(process.getInputStream());
CompletableFuture<List<String>> errFut = readInputStream(process.getErrorStream());
out = outFut.get();
err = errFut.get();
}

int exitCode;
Expand All @@ -171,6 +170,26 @@ public ProcessResult run(ProcessMode processMode) {
}
}

/**
* Asynchronously and parallel reads {@link InputStream input stream} and stores it in {@link CompletableFuture}.
* Inspired by: <a href=
* "https://stackoverflow.com/questions/14165517/processbuilder-forwarding-stdout-and-stderr-of-started-processes-without-blocki/57483714#57483714">StackOverflow</a>
*
* @param is {@link InputStream}.
* @return {@link CompletableFuture}.
*/
private static CompletableFuture<List<String>> readInputStream(InputStream is) {

return CompletableFuture.supplyAsync(() -> {

try (InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr)) {
return br.lines().toList();
} catch (Throwable e) {
throw new RuntimeException("There was a problem while executing the program", e);
}
});
}

private String createCommandMessage(String interpreter, String suffix) {

StringBuilder sb = new StringBuilder();
Expand Down

0 comments on commit 16cf912

Please sign in to comment.