From f868968e312bbf5e38c294193eec9aa64fc8a6f5 Mon Sep 17 00:00:00 2001 From: UNV Date: Tue, 2 Jun 2026 23:36:32 +0300 Subject: [PATCH] Reworking fix ofr unstable GitConfig parsing: avoid wrapping Ini into TreeMap since Ini is technically multi-map and this wrapping can cause data loss. Though this is unlikely the case for remotes, branches and urls, since section name in their case contains name parameter. But anyway better to sort parsed data. Also replacing static final nested config classes with records. --- .../main/java/git4idea/repo/GitConfig.java | 100 +++++++----------- 1 file changed, 39 insertions(+), 61 deletions(-) diff --git a/plugin/src/main/java/git4idea/repo/GitConfig.java b/plugin/src/main/java/git4idea/repo/GitConfig.java index b8023d2..1f8334a 100644 --- a/plugin/src/main/java/git4idea/repo/GitConfig.java +++ b/plugin/src/main/java/git4idea/repo/GitConfig.java @@ -25,6 +25,7 @@ import git4idea.branch.GitBranchUtil; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import org.ini4j.Ini; import org.ini4j.Profile; import java.io.File; @@ -87,7 +88,7 @@ Collection parseRemotes() { private static GitRemote convertRemoteToGitRemote(@Nonnull Collection urls, @Nonnull Remote remote) { UrlsAndPushUrls substitutedUrls = substituteUrls(urls, remote); return new GitRemote( - remote.myName, + remote.name(), substitutedUrls.getUrls(), substitutedUrls.getPushUrls(), remote.getFetchSpecs(), @@ -129,9 +130,9 @@ static GitConfig read(@Nonnull File configFile) { return emptyConfig; } - SortedMap ini; + Ini ini; try { - ini = new TreeMap<>(GitConfigHelper.loadIniFile(configFile)); + ini = GitConfigHelper.loadIniFile(configFile); } catch (IOException e) { return emptyConfig; @@ -144,11 +145,8 @@ static GitConfig read(@Nonnull File configFile) { } @Nonnull - private static Collection parseTrackedInfos( - @Nonnull SortedMap ini, - @Nonnull ClassLoader classLoader - ) { - Collection configs = new ArrayList<>(); + private static Collection parseTrackedInfos(@Nonnull Ini ini, @Nonnull ClassLoader classLoader) { + Collection configs = new TreeSet<>(); for (Map.Entry stringSectionEntry : ini.entrySet()) { String sectionName = stringSectionEntry.getKey(); Profile.Section section = stringSectionEntry.getValue(); @@ -171,10 +169,10 @@ private static GitBranchTrackInfo convertBranchConfig( if (branchConfig == null) { return null; } - String branchName = branchConfig.getName(); - String remoteName = branchConfig.getBean().getRemote(); - String mergeName = branchConfig.getBean().getMerge(); - String rebaseName = branchConfig.getBean().getRebase(); + String branchName = branchConfig.name(); + String remoteName = branchConfig.bean().getRemote(); + String mergeName = branchConfig.bean().getMerge(); + String rebaseName = branchConfig.bean().getRebase(); if (StringUtil.isEmptyOrSpaces(mergeName) && StringUtil.isEmptyOrSpaces(rebaseName)) { LOG.info("No branch." + branchName + ".merge/rebase item in the .git/config"); @@ -240,12 +238,9 @@ private static BranchConfig parseBranchSection(String sectionName, Profile.Secti } @Nonnull - private static Pair, Collection> parseRemotes( - @Nonnull SortedMap ini, - @Nonnull ClassLoader classLoader - ) { - Collection remotes = new ArrayList<>(); - Collection urls = new ArrayList<>(); + private static Pair, Collection> parseRemotes(@Nonnull Ini ini, @Nonnull ClassLoader classLoader) { + Collection remotes = new TreeSet<>(); + Collection urls = new TreeSet<>(); for (Map.Entry stringSectionEntry : ini.entrySet()) { String sectionName = stringSectionEntry.getKey(); Profile.Section section = stringSectionEntry.getValue(); @@ -362,7 +357,7 @@ public List getUrls() { @Nonnull private static String substituteUrl(@Nonnull String remoteUrl, @Nonnull Url url, @Nonnull String insteadOf) { - return url.myName + remoteUrl.substring(insteadOf.length()); + return url.name() + remoteUrl.substring(insteadOf.length()); } @Nullable @@ -387,39 +382,36 @@ private static Url parseUrlSection(@Nonnull String sectionName, @Nonnull Profile return null; } - private static class Remote { - private final String myName; - private final RemoteBean myRemoteBean; - - private Remote(@Nonnull String name, @Nonnull RemoteBean remoteBean) { - myRemoteBean = remoteBean; - myName = name; - } - + private record Remote(String name, RemoteBean remoteBean) implements Comparable { @Nonnull private Collection getUrls() { - return nonNullCollection(myRemoteBean.getUrl()); + return nonNullCollection(remoteBean().getUrl()); } @Nonnull private Collection getPushUrls() { - return nonNullCollection(myRemoteBean.getPushUrl()); + return nonNullCollection(remoteBean().getPushUrl()); } @Nonnull private Collection getPuttyKeys() { - return nonNullCollection(myRemoteBean.getPuttyKeyFile()); + return nonNullCollection(remoteBean().getPuttyKeyFile()); } @Nonnull private List getPushSpec() { - String[] push = myRemoteBean.getPush(); + String[] push = remoteBean().getPush(); return push == null ? Collections.emptyList() : Arrays.asList(push); } @Nonnull private List getFetchSpecs() { - return Arrays.asList(notNull(myRemoteBean.getFetch())); + return Arrays.asList(notNull(remoteBean().getFetch())); + } + + @Override + public int compareTo(Remote that) { + return name().compareTo(that.name()); } } @@ -440,25 +432,22 @@ private interface RemoteBean { String[] getPuttyKeyFile(); } - private static class Url { - private final String myName; - private final UrlBean myUrlBean; - - private Url(String name, UrlBean urlBean) { - myUrlBean = urlBean; - myName = name; - } - - @Nullable + private record Url(String name, UrlBean urlBean) implements Comparable { // null means to entry, i.e. nothing to substitute. Empty string means substituting everything + @Nullable public String getInsteadOf() { - return myUrlBean.getInsteadOf(); + return urlBean().getInsteadOf(); } - @Nullable // null means to entry, i.e. nothing to substitute. Empty string means substituting everything + @Nullable public String getPushInsteadOf() { - return myUrlBean.getPushInsteadOf(); + return urlBean().getPushInsteadOf(); + } + + @Override + public int compareTo(Url that) { + return name().compareTo(that.name()); } } @@ -470,21 +459,10 @@ private interface UrlBean { String getPushInsteadOf(); } - private static class BranchConfig { - private final String myName; - private final BranchBean myBean; - - public BranchConfig(String name, BranchBean bean) { - myName = name; - myBean = bean; - } - - public String getName() { - return myName; - } - - public BranchBean getBean() { - return myBean; + private record BranchConfig(String name, BranchBean bean) implements Comparable { + @Override + public int compareTo(BranchConfig that) { + return name().compareTo(that.name()); } }