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

devonfw / IDEasy / 13327588889

14 Feb 2025 10:44AM UTC coverage: 67.947% (-0.5%) from 68.469%
13327588889

Pull #1021

github

web-flow
Merge d03159bfe into 52609dacb
Pull Request #1021: #786: support ide upgrade to automatically update to the latest version of IDEasy

2964 of 4791 branches covered (61.87%)

Branch coverage included in aggregate %.

7688 of 10886 relevant lines covered (70.62%)

3.07 hits per line

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

92.5
cli/src/main/java/com/devonfw/tools/ide/git/GitOperation.java
1
package com.devonfw.tools.ide.git;
2

3
import java.nio.file.Files;
4
import java.nio.file.Path;
5
import java.time.Duration;
6

7
import com.devonfw.tools.ide.context.IdeContext;
8

9
/**
10
 * An {@link Enum} for specific Git operations where we add caching support.
11
 *
12
 * @see GitContextImpl
13
 */
14
public enum GitOperation {
3✔
15

16
  /** {@link GitOperation} for {@link GitContext#fetch(Path, String, String)}. */
17
  FETCH("fetch", "FETCH_HEAD", Duration.ofMinutes(5)) {
18✔
18
    @Override
19
    protected boolean execute(IdeContext context, GitUrl gitUrl, Path targetRepository, String remote) {
20

21
      context.getGitContext().fetch(targetRepository, remote, gitUrl.branch());
7✔
22
      // TODO: see JavaDoc, implementation incorrect. fetch needs to return boolean if changes have been fetched
23
      // and then this result must be returned - or JavaDoc needs to changed
24
      return true;
2✔
25
    }
26
  },
27

28
  /** {@link GitOperation} for {@link GitContext#clone(GitUrl, Path)}. */
29
  PULL_OR_CLONE("pull/clone", "HEAD", Duration.ofMinutes(30)) {
18✔
30
    @Override
31
    protected boolean execute(IdeContext context, GitUrl gitUrl, Path targetRepository, String remote) {
32

33
      context.getGitContext().pullOrClone(gitUrl, targetRepository);
5✔
34
      return true;
2✔
35
    }
36
  };
37

38
  private final String name;
39

40
  private final String timestampFilename;
41

42
  private final Duration cacheDuration;
43

44
  private GitOperation(String name, String timestampFilename, Duration cacheDuration) {
4✔
45

46
    this.name = name;
3✔
47
    this.timestampFilename = timestampFilename;
3✔
48
    this.cacheDuration = cacheDuration;
3✔
49
  }
1✔
50

51
  /**
52
   * @return the human readable name of this {@link GitOperation}.
53
   */
54
  public String getName() {
55

56
    return this.name;
×
57
  }
58

59
  /**
60
   * @return the name of the file inside the ".git" folder to get the timestamp (modification time) from in order to determine how long the last
61
   *     {@link GitOperation} was ago.
62
   */
63
  public String getTimestampFilename() {
64

65
    return this.timestampFilename;
3✔
66
  }
67

68
  /**
69
   * @return the {@link Duration} how long this {@link GitOperation} will be skipped.
70
   */
71
  public Duration getCacheDuration() {
72

73
    return cacheDuration;
×
74
  }
75

76
  /**
77
   * @return {@code true} if this operation requires the ".git" folder to be present, {@code false} otherwise.
78
   */
79
  public boolean isRequireGitFolder() {
80

81
    return this == FETCH;
6!
82
  }
83

84
  /**
85
   * @return {@code true} if after this operation the {@link #getTimestampFilename() timestamp file} should be updated, {@code false} otherwise.
86
   */
87
  public boolean isForceUpdateTimestampFile() {
88

89
    return this == PULL_OR_CLONE;
7✔
90
  }
91

92
  /**
93
   * @return {@code true} if after this operation is always {@link #isNeeded(Path, IdeContext) needed} of the ".git" folder not is present, {@code false}
94
   *     otherwise.
95
   */
96
  public boolean isNeededIfGitFolderNotPresent() {
97

98
    return this == PULL_OR_CLONE;
7✔
99
  }
100

101
  /**
102
   * Executes this {@link GitOperation} physically.
103
   *
104
   * @param context the {@link IdeContext}.
105
   * @param gitUrl the git repository URL. Maybe {@code null} if not required by the operation.
106
   * @param targetRepository the {@link Path} to the git repository.
107
   * @param remote the git remote (e.g. "origin"). Maybe {@code null} if not required by the operation.
108
   * @return {@code true} if changes were received from git, {@code false} otherwise.
109
   */
110
  protected abstract boolean execute(IdeContext context, GitUrl gitUrl, Path targetRepository, String remote);
111

112
  /**
113
   * Executes this {@link GitOperation} if {@link #isNeeded(Path, IdeContext) needed}.
114
   *
115
   * @param context the {@link IdeContext}.
116
   * @param gitUrl the git repository URL. Maybe {@code null} if not required by the operation.
117
   * @param targetRepository the {@link Path} to the git repository.
118
   * @param remote the git remote (e.g. "origin"). Maybe {@code null} if not required by the operation.
119
   * @return {@code true} if changes were received from git, {@code false} otherwise (e.g. no git operation was invoked at all).
120
   */
121
  boolean executeIfNeeded(IdeContext context, GitUrl gitUrl, Path targetRepository, String remote) {
122

123
    if (isNeeded(targetRepository, context)) {
5✔
124
      boolean result = execute(context, gitUrl, targetRepository, remote);
7✔
125
      if (isForceUpdateTimestampFile()) {
3✔
126
        Path timestampPath = targetRepository.resolve(GitContext.GIT_FOLDER).resolve(this.timestampFilename);
7✔
127
        try {
128
          context.getFileAccess().touch(timestampPath);
4✔
129
        } catch (IllegalStateException e) {
1✔
130
          context.warning(e.getMessage());
4✔
131
        }
1✔
132
      }
133
      return result;
2✔
134
    } else {
135
      context.trace("Skipped git {}.", this.name);
10✔
136
      return false;
2✔
137
    }
138
  }
139

140
  private boolean isNeeded(Path targetRepository, IdeContext context) {
141

142
    Path gitDirectory = targetRepository.resolve(".git");
4✔
143
    boolean hasGitDirectory = Files.isDirectory(gitDirectory);
5✔
144
    if (isNeededIfGitFolderNotPresent() && !hasGitDirectory) {
5✔
145
      logEnforceGitOperationBecauseGitFolderNotPresent(targetRepository, context);
4✔
146
      return true;
2✔
147
    }
148
    if (context.isOffline()) {
3✔
149
      context.info("Skipping git {} on {} because we are offline.", this.name, targetRepository);
14✔
150
      return false;
2✔
151
    } else if (context.isForceMode()) {
3✔
152
      context.debug("Enforcing git {} on {} because force mode is active.", this.name, targetRepository);
14✔
153
      return true;
2✔
154
    }
155
    if (!hasGitDirectory) {
2✔
156
      if (isRequireGitFolder()) {
3!
157
        if (context.getSettingsGitRepository() == null) {
3!
158
          context.warning("Missing .git folder in {}.", targetRepository);
10✔
159
        }
160
      } else {
161
        logEnforceGitOperationBecauseGitFolderNotPresent(targetRepository, context);
×
162
      }
163
      return true; // technically this is an error that will be triggered by fetch method
2✔
164
    }
165
    Path timestampFilePath = gitDirectory.resolve(this.timestampFilename);
5✔
166
    if (context.getFileAccess().isFileAgeRecent(timestampFilePath, this.cacheDuration)) {
7✔
167
      context.debug("Skipping git {} on {} because last fetch was just recently to avoid overhead.", this.name,
14✔
168
          targetRepository);
169
      return false;
2✔
170
    } else {
171
      context.debug("Will need to do git {} on {} because last fetch is some time ago.", this.name, targetRepository);
14✔
172
      return true;
2✔
173
    }
174
  }
175

176
  private void logEnforceGitOperationBecauseGitFolderNotPresent(Path targetRepository, IdeContext context) {
177
    context.debug("Enforcing git {} on {} because .git folder is not present.", this.name, targetRepository);
14✔
178
  }
1✔
179

180
}
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