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

devonfw / IDEasy / 12995336297

27 Jan 2025 06:04PM UTC coverage: 68.45% (-0.05%) from 68.499%
12995336297

push

github

web-flow
#931: enhance settings in code repository (#983)

Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com>
Co-authored-by: Jörg Hohwiller <hohwille@users.noreply.github.com>

2793 of 4475 branches covered (62.41%)

Branch coverage included in aggregate %.

7211 of 10140 relevant lines covered (71.11%)

3.09 hits per line

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

90.22
cli/src/main/java/com/devonfw/tools/ide/git/GitOperation.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.time.Duration;
7

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

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

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

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

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

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

39
  private final String name;
40

41
  private final String timestampFilename;
42

43
  private final Duration cacheDuration;
44

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

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

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

57
    return this.name;
×
58
  }
59

60
  /**
61
   * @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
62
   *     {@link GitOperation} was ago.
63
   */
64
  public String getTimestampFilename() {
65

66
    return this.timestampFilename;
3✔
67
  }
68

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

74
    return cacheDuration;
×
75
  }
76

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

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

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

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

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

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

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

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

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

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

143
    Path gitDirectory = targetRepository.resolve(".git");
4✔
144
    boolean hasGitDirectory = Files.isDirectory(gitDirectory);
5✔
145
    if (isNeededIfGitFolderNotPresent() && !hasGitDirectory) {
5✔
146
      logEnforceGitOperationBecauseGitFolderNotPresent(targetRepository, context);
4✔
147
      return true;
2✔
148
    }
149
    if (context.isOffline()) {
3✔
150
      context.info("Skipping git {} on {} because we are offline.", this.name, targetRepository);
14✔
151
      return false;
2✔
152
    } else if (context.isForceMode()) {
3✔
153
      context.debug("Enforcing git {} on {} because force mode is active.", this.name, targetRepository);
14✔
154
      return true;
2✔
155
    }
156
    if (!hasGitDirectory) {
2✔
157
      if (isRequireGitFolder()) {
3!
158
        if (context.getSettingsGitRepository() == null) {
3!
159
          context.warning("Missing .git folder in {}.", targetRepository);
10✔
160
        }
161
      } else {
162
        logEnforceGitOperationBecauseGitFolderNotPresent(targetRepository, context);
×
163
      }
164
      return true; // technically this is an error that will be triggered by fetch method
2✔
165
    }
166
    Path timestampFilePath = gitDirectory.resolve(this.timestampFilename);
5✔
167
    if (Files.exists(timestampFilePath)) {
5✔
168
      long currentTime = System.currentTimeMillis();
2✔
169
      try {
170
        long fileModifiedTime = Files.getLastModifiedTime(timestampFilePath).toMillis();
6✔
171
        // Check if the file modification time is older than the delta threshold
172
        Duration lastFileUpdateDuration = Duration.ofMillis(currentTime - fileModifiedTime);
5✔
173
        context.debug("In git repository {} the timestamp file {} was last updated {} ago and caching duration in {}.", targetRepository,
23✔
174
            timestampFilename, lastFileUpdateDuration, this.cacheDuration);
175
        if ((lastFileUpdateDuration.toMillis() > this.cacheDuration.toMillis())) {
7✔
176
          context.debug("Will need to do git {} on {} because last fetch is some time ago.", this.name, targetRepository);
14✔
177
          return true;
2✔
178
        } else {
179
          context.debug("Skipping git {} on {} because last fetch was just recently to avoid overhead.", this.name,
14✔
180
              targetRepository);
181
          return false;
2✔
182
        }
183
      } catch (IOException e) {
×
184
        context.warning().log(e, "Could not update modification-time of {}. Will have to do git {}.", timestampFilePath, this.name);
×
185
        return true;
×
186
      }
187
    } else {
188
      context.debug("Will need to do git {} on {} because {} is missing.", this.name, targetRepository, timestampFilename);
19✔
189
      return true;
2✔
190
    }
191
  }
192

193
  private void logEnforceGitOperationBecauseGitFolderNotPresent(Path targetRepository, IdeContext context) {
194
    context.debug("Enforcing git {} on {} because .git folder is not present.", this.name, targetRepository);
14✔
195
  }
1✔
196

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