• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 14462897556

15 Apr 2025 06:35AM UTC coverage: 67.461% (+0.1%) from 67.315%
14462897556

Pull #1241

github

web-flow
Merge 6bd21c956 into 1823d2ec6
Pull Request #1241: #1205 : Fix Git implementation

3055 of 4930 branches covered (61.97%)

Branch coverage included in aggregate %.

7879 of 11278 relevant lines covered (69.86%)

3.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.41
cli/src/main/java/com/devonfw/tools/ide/git/GitContextImpl.java
1
package com.devonfw.tools.ide.git;
2

3
import java.io.IOException;
4
import java.nio.file.Files;
5
import java.nio.file.Path;
6
import java.util.List;
7
import java.util.Objects;
8

9
import com.devonfw.tools.ide.cli.CliException;
10
import com.devonfw.tools.ide.context.IdeContext;
11
import com.devonfw.tools.ide.process.ProcessMode;
12
import com.devonfw.tools.ide.process.ProcessResult;
13
import com.devonfw.tools.ide.variable.IdeVariables;
14

15
/**
16
 * Implements the {@link GitContext}.
17
 */
18
public class GitContextImpl implements GitContext {
19

20
  private final IdeContext context;
21

22
  /**
23
   * @param context the {@link IdeContext context}.
24
   */
25
  public GitContextImpl(IdeContext context) {
2✔
26

27
    this.context = context;
3✔
28
  }
1✔
29

30
  @Override
31
  public void pullOrCloneIfNeeded(GitUrl gitUrl, Path repository) {
32
    executeGitOperation(GitOperation.PULL_OR_CLONE, gitUrl, repository, null);
7✔
33
  }
1✔
34

35
  @Override
36
  public boolean fetchIfNeeded(Path repository) {
37
    return executeGitOperation(GitOperation.FETCH, new GitUrl("https://dummy.url/repo.git", null), repository, null);
×
38
  }
39

40
  @Override
41
  public boolean fetchIfNeeded(Path repository, String remote, String branch) {
42
    return executeGitOperation(GitOperation.FETCH, new GitUrl("https://dummy.url/repo.git", branch), repository, remote);
11✔
43
  }
44

45
  @Override
46
  public boolean isRepositoryUpdateAvailable(Path repository) {
47
    return compareCommitIds(repository, "HEAD", "@{u}");
6✔
48
  }
49

50
  @Override
51
  public boolean isRepositoryUpdateAvailable(Path repository, Path trackedCommitIdPath) {
52
    String trackedCommitId = readFileContent(trackedCommitIdPath);
×
53
    String remoteCommitId = getCommitId(repository, "@{u}");
×
54
    return !Objects.equals(trackedCommitId, remoteCommitId);
×
55
  }
56

57
  @Override
58
  public void pullOrCloneAndResetIfNeeded(GitUrl gitUrl, Path repository, String remoteName) {
59

60
    pullOrCloneIfNeeded(gitUrl, repository);
4✔
61
    reset(repository, gitUrl.branch(), remoteName);
6✔
62
    cleanup(repository);
3✔
63
  }
1✔
64

65
  @Override
66
  public void pullOrClone(GitUrl gitUrl, Path repository) {
67

68
    Objects.requireNonNull(repository);
3✔
69
    Objects.requireNonNull(gitUrl);
3✔
70
    if (isGitRepository(repository)) {
4✔
71
      String remote = determineRemote(repository);
4✔
72
      if (remote == null) {
2!
73
        this.context.askToContinue(repository + " is a local git repository with no remote. Do you want to continue?");
×
74
      } else {
75
        pull(repository);
3✔
76
      }
77
    } else {
1✔
78
      clone(gitUrl, repository);
4✔
79
    }
80
  }
1✔
81

82
  @Override
83
  public void clone(GitUrl gitUrl, Path repository) {
84

85
    verifyGitInstalled();
2✔
86
    gitUrl = IdeVariables.PREFERRED_GIT_PROTOCOL.get(context).format(gitUrl);
8✔
87
    requireOnline("git clone of " + gitUrl);
5✔
88
    this.context.getFileAccess().mkdirs(repository);
5✔
89
    List<String> args = List.of("clone", "--recursive", gitUrl.url(), "--config", "core.autocrlf=false", ".");
9✔
90
    runGitCommand(repository, args);
5✔
91
    switchBranch(repository, gitUrl.branch());
5✔
92
  }
1✔
93

94
  @Override
95
  public void pull(Path repository) {
96

97
    verifyGitInstalled();
2✔
98
    if (this.context.isOffline()) {
4!
99
      this.context.info("Skipping git pull on {} because offline", repository);
×
100
      return;
×
101
    }
102
    ProcessResult result = runGitCommand(repository, ProcessMode.DEFAULT, "--no-pager", "pull", "--quiet");
19✔
103
    handleErrors(repository, result, "Git pull failed.");
5✔
104
  }
1✔
105

106
  @Override
107
  public void fetch(Path repository, String remote, String branch) {
108

109
    verifyGitInstalled();
2✔
110
    runGitCommand(repository, ProcessMode.DEFAULT_CAPTURE, "fetch", Objects.requireNonNullElse(remote, "origin"), branch);
22✔
111
  }
1✔
112

113
  @Override
114
  public String determineCurrentBranch(Path repository) {
115
    return getCommitId(repository, "branch", "--show-current");
×
116
  }
117

118
  @Override
119
  public String determineRemote(Path repository) {
120
    return getCommitId(repository, "remote");
10✔
121
  }
122

123
  @Override
124
  public void reset(Path repository, String branchName, String remoteName) {
125

126
    verifyGitInstalled();
2✔
127
    branchName = Objects.requireNonNullElse(branchName, GitUrl.BRANCH_MASTER);
5✔
128
    remoteName = Objects.requireNonNullElse(remoteName, DEFAULT_REMOTE);
5✔
129
    ProcessResult result = runGitCommand(repository, ProcessMode.DEFAULT, "diff-index", "--quiet", "HEAD");
19✔
130
    if (!result.isSuccessful()) {
3!
131
      this.context.warning("Resetting {} to {}/{}.", repository, remoteName, branchName);
18✔
132
      runGitCommand(repository, ProcessMode.DEFAULT, "reset", "--hard", remoteName + "/" + branchName);
21✔
133
    }
134
  }
1✔
135

136
  @Override
137
  public void cleanup(Path repository) {
138

139
    verifyGitInstalled();
2✔
140
    // check for untracked files
141
    ProcessResult result = runGitCommand(repository, ProcessMode.DEFAULT_CAPTURE, "ls-files", "--other", "--directory", "--exclude-standard");
23✔
142
    if (!result.getOut().isEmpty()) {
4✔
143
      this.context.warning("Cleaning up untracked files in {}.", repository);
10✔
144
      runGitCommand(repository, "clean", "-df");
14✔
145
    }
146
  }
1✔
147

148
  @Override
149
  public String retrieveGitUrl(Path repository) {
150
    return getCommitId(repository, "config", "--get", "remote.origin.url");
×
151
  }
152

153
  @Override
154
  public void saveCurrentCommitId(Path repository, Path trackedCommitIdPath) {
155
    String currentCommitId = getCommitId(repository, "rev-parse", "HEAD");
×
156
    if (currentCommitId != null) {
×
157
      writeFileContent(trackedCommitIdPath, currentCommitId);
×
158
    }
159
  }
×
160

161
  private boolean executeGitOperation(GitOperation operation, GitUrl gitUrl, Path repository, String remote) {
162
    return operation.executeIfNeeded(this.context, gitUrl, repository, remote);
8✔
163
  }
164

165
  private boolean compareCommitIds(Path repository, String localRef, String remoteRef) {
166
    String localCommitId = getCommitId(repository, "rev-parse", localRef);
14✔
167
    String remoteCommitId = getCommitId(repository, "rev-parse", remoteRef);
14✔
168
    return !Objects.equals(localCommitId, remoteCommitId);
6!
169
  }
170

171
  private String getCommitId(Path repository, String... args) {
172
    ProcessResult result = runGitCommand(repository, ProcessMode.DEFAULT_CAPTURE, args);
6✔
173
    return result.isSuccessful() && !result.getOut().isEmpty() ? result.getOut().getFirst() : null;
14!
174
  }
175

176
  private void switchBranch(Path repository, String branch) {
177
    if (branch != null) {
2!
178
      runGitCommand(repository, "switch", branch);
×
179
    }
180
  }
1✔
181

182
  private void handleErrors(Path repository, ProcessResult result, String errorMessage) {
183
    if (!result.isSuccessful()) {
3!
184
      this.context.warning(errorMessage);
×
185
      if (this.context.isOnline()) {
×
186
        this.context.error("See above error for details. If you have local changes, please stash or revert and retry.");
×
187
      } else {
188
        this.context.error("Ensure Internet connectivity and retry or activate offline mode.");
×
189
      }
190
      this.context.askToContinue("Do you want to continue anyway?");
×
191
    }
192
  }
1✔
193

194
  private void verifyGitInstalled() {
195
    Path git = Path.of("git");
5✔
196
    if (this.context.getPath().findBinary(git) == git) {
7!
197
      throw new CliException("Git installation not found. Please install git.");
×
198
    }
199
  }
1✔
200

201
  private void requireOnline(String action) {
202
    if (this.context.isOfflineMode()) {
4✔
203
      this.context.requireOnline(action);
×
204
    }
205
  }
1✔
206

207
  private String readFileContent(Path path) {
208
    try {
209
      return Files.readString(path);
×
210
    } catch (IOException e) {
×
211
      this.context.warning("Failed to read file: {}", path);
×
212
      return null;
×
213
    }
214
  }
215

216
  private void writeFileContent(Path path, String content) {
217
    try {
218
      Files.writeString(path, content);
×
219
    } catch (IOException e) {
×
220
      throw new IllegalStateException("Failed to write file: " + path, e);
×
221
    }
×
222
  }
×
223

224
  private boolean isGitRepository(Path repository) {
225
    return Files.isDirectory(repository.resolve(GIT_FOLDER));
7✔
226
  }
227

228
  private ProcessResult runGitCommand(Path directory, ProcessMode mode, String... args) {
229
    return this.context.newProcess()
5✔
230
        .executable("git")
3✔
231
        .withEnvVar("GIT_TERMINAL_PROMPT", "0")
2✔
232
        .directory(directory)
2✔
233
        .addArgs(args)
2✔
234
        .run(mode);
1✔
235
  }
236

237
  private ProcessResult runGitCommand(Path directory, List<String> args) {
238
    return runGitCommand(directory, ProcessMode.DEFAULT, args.toArray(String[]::new));
12✔
239
  }
240

241
  private ProcessResult runGitCommand(Path directory, String... args) {
242
    return runGitCommand(directory, ProcessMode.DEFAULT, args);
6✔
243
  }
244
}
245

246

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc