Skip to content

Commit

Permalink
Merge d746a46 into 023f8de
Browse files Browse the repository at this point in the history
  • Loading branch information
mvomiero authored May 10, 2024
2 parents 023f8de + d746a46 commit 0953425
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,18 @@ public String askForInput(String message, String defaultValue) {
return input.isEmpty() ? defaultValue : input;
}

@Override
public String askForInput(String message) {

String input;
do {
info(message);
input = readLine().trim();
} while (input.isEmpty());

return input;
}

@SuppressWarnings("unchecked")
@Override
public <O> O question(String question, O... options) {
Expand Down
78 changes: 38 additions & 40 deletions cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.devonfw.tools.ide.context;

import java.nio.file.Path;

import com.devonfw.tools.ide.cli.CliOfflineException;

import java.nio.file.Path;

/**
* Interface for git commands with input and output of information for the user.
*/
Expand All @@ -12,15 +12,16 @@ public interface GitContext {
/** The default git remote name. */
String DEFAULT_REMOTE = "origin";

/** The default git url of the settings repository for IDEasy developers */
String DEFAULT_SETTINGS_GIT_URL = "https://github.com/devonfw/ide-settings.git";

/**
* Checks if the Git repository in the specified target folder needs an update by inspecting the modification time of
* a magic file.
* Checks if the Git repository in the specified target folder needs an update by inspecting the modification time of a magic file.
*
* @param repoUrl the git remote URL to clone from.
* @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch.
* @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.
* @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.
* @throws CliOfflineException if offline and cloning is needed.
*/
void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository);
Expand All @@ -29,9 +30,8 @@ public interface GitContext {
* Attempts a git pull and reset if required.
*
* @param repoUrl the git remote URL to clone from.
* @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.
* @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.
* @throws CliOfflineException if offline and cloning is needed.
*/
default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository) {
Expand All @@ -43,9 +43,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository)
* Attempts a git pull and reset if required.
*
* @param repoUrl the git remote URL to clone from.
* @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.
* @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.
* @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch.
* @throws CliOfflineException if offline and cloning is needed.
*/
Expand All @@ -58,9 +57,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository,
* Attempts a git pull and reset if required.
*
* @param repoUrl the git remote URL to clone from.
* @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.
* @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.
* @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch.
* @param remoteName the remote name e.g. origin.
* @throws CliOfflineException if offline and cloning is needed.
Expand All @@ -71,9 +69,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository,
* Runs a git pull or a git clone.
*
* @param gitRepoUrl the git remote URL to clone from.
* @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.
* @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.
* @throws CliOfflineException if offline and cloning is needed.
*/
void pullOrClone(String gitRepoUrl, Path targetRepository);
Expand All @@ -83,9 +80,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository,
*
* @param gitRepoUrl the git remote URL to clone from.
* @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch.
* @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.
* @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.
* @throws CliOfflineException if offline and cloning is needed.
*/
void pullOrClone(String gitRepoUrl, String branch, Path targetRepository);
Expand All @@ -94,28 +90,25 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository,
* Runs a git clone.
*
* @param gitRepoUrl the {@link GitUrl} to use for the repository URL.
* @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.
* @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.
* @throws CliOfflineException if offline and cloning is needed.
*/
void clone(GitUrl gitRepoUrl, Path targetRepository);

/**
* Runs a 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.
* @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.
*/
void pull(Path targetRepository);

/**
* Runs a git diff-index to detect local changes and if so reverts them via git reset.
*
* @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.
* @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.
*/
default void reset(Path targetRepository) {

Expand All @@ -125,9 +118,8 @@ default void reset(Path targetRepository) {
/**
* Runs a git diff-index to detect local changes and if so reverts them via git reset.
*
* @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.
* @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.
* @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch.
*/
default void reset(Path targetRepository, String branch) {
Expand All @@ -138,9 +130,8 @@ default void reset(Path targetRepository, String branch) {
/**
* Runs a git reset reverting all local changes to the git repository.
*
* @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.
* @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.
* @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch.
* @param remoteName the name of the git remote e.g. "origin".
*/
Expand All @@ -149,10 +140,17 @@ default void reset(Path targetRepository, String branch) {
/**
* Runs a git cleanup if untracked files were found.
*
* @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.
* @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.
*/
void cleanup(Path targetRepository);

/**
* Returns the URL of a git repository
*
* @param repository the {@link Path} to the folder where the git repository is located.
* @return the url of the repository as a {@link String}.
*/
String retrieveGitUrl(Path repository);

}
65 changes: 36 additions & 29 deletions cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.devonfw.tools.ide.context;

import com.devonfw.tools.ide.cli.CliOfflineException;
import com.devonfw.tools.ide.process.ProcessContext;
import com.devonfw.tools.ide.process.ProcessErrorHandling;
import com.devonfw.tools.ide.process.ProcessMode;
import com.devonfw.tools.ide.process.ProcessResult;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
Expand All @@ -12,12 +18,6 @@
import java.util.Map;
import java.util.Objects;

import com.devonfw.tools.ide.cli.CliOfflineException;
import com.devonfw.tools.ide.process.ProcessContext;
import com.devonfw.tools.ide.process.ProcessErrorHandling;
import com.devonfw.tools.ide.process.ProcessMode;
import com.devonfw.tools.ide.process.ProcessResult;

/**
* Implements the {@link GitContext}.
*/
Expand Down Expand Up @@ -101,8 +101,7 @@ public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository)
ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE);
List<String> remotes = result.getOut();
if (remotes.isEmpty()) {
String message = targetRepository
+ " is a local git repository with no remote - if you did this for testing, you may continue...\n"
String message = targetRepository + " is a local git repository with no remote - if you did this for testing, you may continue...\n"
+ "Do you want to ignore the problem and continue anyhow?";
this.context.askToContinue(message);
} else {
Expand All @@ -120,9 +119,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.
* @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.
* @param result the {@link ProcessResult} to evaluate.
*/
private void handleErrors(Path targetRepository, ProcessResult result) {
Expand All @@ -135,11 +133,9 @@ 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).");
this.context.error("It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline).");
}
this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?");
}
Expand Down Expand Up @@ -173,8 +169,7 @@ public void clone(GitUrl gitRepoUrl, Path targetRepository) {
}
}
} else {
throw new CliOfflineException(
"Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline.");
throw new CliOfflineException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline.");
}
}

Expand All @@ -188,8 +183,8 @@ public void pull(Path targetRepository) {

if (!result.isSuccessful()) {
Map<String, String> remoteAndBranchName = retrieveRemoteAndBranchName();
this.context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"),
remoteAndBranchName.get("branch"), targetRepository);
this.context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), remoteAndBranchName.get("branch"),
targetRepository);
handleErrors(targetRepository, result);
}
}
Expand All @@ -206,17 +201,15 @@ private Map<String, String> retrieveRemoteAndBranchName() {
remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/")));
// check if current repo is behind remote and omit message
if (checkedOutBranch.contains(":")) {
remoteAndBranchName.put("branch",
checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":")));
remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":")));
} else {
remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1));
}

}
}
} else {
return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"),
new AbstractMap.SimpleEntry<>("branch", "unknown"));
return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), new AbstractMap.SimpleEntry<>("branch", "unknown"));
}

return remoteAndBranchName;
Expand All @@ -235,10 +228,8 @@ public void reset(Path targetRepository, String branchName, String remoteName) {

if (!result.isSuccessful()) {
// reset to origin/master
this.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(PROCESS_MODE);
this.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(PROCESS_MODE);

if (!result.isSuccessful()) {
this.context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository);
Expand All @@ -253,8 +244,7 @@ public void cleanup(Path 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);
result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard").run(ProcessMode.DEFAULT_CAPTURE);

if (!result.getOut().isEmpty()) {
// delete untracked files
Expand All @@ -267,4 +257,21 @@ public void cleanup(Path targetRepository) {
}
}

@Override
public String retrieveGitUrl(Path repository) {

this.processContext.directory(repository);
ProcessResult result;
result = this.processContext.addArgs("-C", repository, "remote", "-v").run(ProcessMode.DEFAULT_CAPTURE);
for (String line : result.getOut()) {
if (line.contains("(fetch)")) {
return line.split("\\s+")[1]; // Extract the URL from the line
}
}

this.context.error("Failed to retrieve git URL for repository: {}", repository);
return null;
}
}


Loading

0 comments on commit 0953425

Please sign in to comment.