Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions plugin/src/main/java/git4idea/repo/GitConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import git4idea.branch.GitBranchUtil;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.ini4j.Ini;
import org.ini4j.Profile;

import java.io.File;
Expand Down Expand Up @@ -58,7 +57,6 @@ public class GitConfig {
@Nonnull
private final Collection<BranchConfig> myTrackedInfos;


private GitConfig(@Nonnull Collection<Remote> remotes, @Nonnull Collection<Url> urls, @Nonnull Collection<BranchConfig> trackedInfos) {
myRemotes = remotes;
myUrls = urls;
Expand All @@ -81,10 +79,7 @@ Collection<GitRemote> parseRemotes() {
// populate GitRemotes with substituting urls when needed
return ContainerUtil.map(
myRemotes,
remote -> {
assert remote != null;
return convertRemoteToGitRemote(myUrls, remote);
}
remote -> convertRemoteToGitRemote(myUrls, Objects.requireNonNull(remote))
);
}

Expand Down Expand Up @@ -134,9 +129,9 @@ static GitConfig read(@Nonnull File configFile) {
return emptyConfig;
}

Ini ini;
SortedMap<String, Profile.Section> ini;
try {
ini = GitConfigHelper.loadIniFile(configFile);
ini = new TreeMap<>(GitConfigHelper.loadIniFile(configFile));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good root-cause fix — wrapping in a TreeMap makes section iteration order deterministic, so two remotes (e.g. origin/upstream) always parse into the same list order and the equality check no longer flip-flops, which is what was driving the infinite git-log refresh loop.

Two notes for consideration (both non-blocking):

  1. TreeMap vs LinkedHashMapTreeMap sorts sections alphabetically, which is perfectly deterministic and fine here. If you'd prefer to preserve the on-disk .git/config order, new LinkedHashMap<>(GitConfigHelper.loadIniFile(configFile)) is equally deterministic. Either is correct; just flagging the behavioral choice.

  2. ini4j Ini is a MultiMap — copying it into a plain Map collapses any duplicate section keys to a single entry (last wins), whereas Ini.get() returns the first. Real .git/config files don't repeat a section name, so this is only an edge case, but worth being aware of.

}
catch (IOException e) {
return emptyConfig;
Expand All @@ -149,7 +144,10 @@ static GitConfig read(@Nonnull File configFile) {
}

@Nonnull
private static Collection<BranchConfig> parseTrackedInfos(@Nonnull Ini ini, @Nonnull ClassLoader classLoader) {
private static Collection<BranchConfig> parseTrackedInfos(
@Nonnull SortedMap<String, Profile.Section> ini,
@Nonnull ClassLoader classLoader
) {
Collection<BranchConfig> configs = new ArrayList<>();
for (Map.Entry<String, Profile.Section> stringSectionEntry : ini.entrySet()) {
String sectionName = stringSectionEntry.getKey();
Expand Down Expand Up @@ -242,7 +240,10 @@ private static BranchConfig parseBranchSection(String sectionName, Profile.Secti
}

@Nonnull
private static Pair<Collection<Remote>, Collection<Url>> parseRemotes(@Nonnull Ini ini, @Nonnull ClassLoader classLoader) {
private static Pair<Collection<Remote>, Collection<Url>> parseRemotes(
@Nonnull SortedMap<String, Profile.Section> ini,
@Nonnull ClassLoader classLoader
) {
Collection<Remote> remotes = new ArrayList<>();
Collection<Url> urls = new ArrayList<>();
for (Map.Entry<String, Profile.Section> stringSectionEntry : ini.entrySet()) {
Expand Down
Loading