Skip to content

Commit 728a4a7

Browse files
committed
fix: GitFlow sidebar not refreshing immediately when enabled
Add PropertyChanged listener to trigger UpdateLeftSidebarLayout() when ShowGitFlowInSidebar property changes. This ensures GitFlow branches appear immediately when the option is enabled in settings, without requiring manual collapse/expand or window resize. - Listen for ShowGitFlowInSidebar property changes - Trigger UI layout update on property change - Add proper cleanup in OnUnloaded to prevent memory leaks - Works for both 'main' and 'master' branch repositories
1 parent fd8fc56 commit 728a4a7

File tree

4 files changed

+304
-22
lines changed

4 files changed

+304
-22
lines changed

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"Bash(git log:*)",
1616
"Bash(git cherry-pick:*)",
1717
"Bash(find:*)",
18-
"Bash(dotnet test:*)"
18+
"Bash(dotnet test:*)",
19+
"Bash(git add:*)"
1920
],
2021
"deny": [],
2122
"ask": [],

src/ViewModels/Repository.Refresh.cs

Lines changed: 198 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public void RefreshAll()
134134
};
135135

136136
// Fire and forget - these will complete asynchronously
137-
Task.WhenAll(parallelTasks).ContinueWith(t =>
137+
Task.WhenAll(parallelTasks).ContinueWith(async t =>
138138
{
139139
if (t.IsFaulted)
140140
{
@@ -149,20 +149,32 @@ public void RefreshAll()
149149
{
150150
var totalTime = Models.PerformanceMonitor.StopTimer("RefreshAll");
151151
System.Diagnostics.Debug.WriteLine($"[PERF] RefreshAll completed in {totalTime}ms");
152-
152+
153153
// Log summary periodically
154154
if (totalTime > 0 && Models.PerformanceMonitor.GetAverageTime("RefreshAll") > 0)
155155
{
156156
var summary = Models.PerformanceMonitor.GetPerformanceSummary();
157157
System.Diagnostics.Debug.WriteLine(summary);
158158
}
159+
160+
// Load GitFlow configuration after branches are loaded
161+
await LoadGitFlowConfigAsync();
162+
163+
// Update GitFlow branches if enabled
164+
if (_settings != null && _settings.ShowGitFlowInSidebar && _branches != null)
165+
{
166+
var localBranches = _branches.Where(b => b.IsLocal && !b.IsDetachedHead).ToList();
167+
ExecuteOnUIThread(() =>
168+
{
169+
UpdateGitFlowBranches(localBranches);
170+
});
171+
}
159172
}
160173
});
161174

162175
Task.Run(async () =>
163176
{
164177
await LoadIssueTrackersAsync();
165-
await LoadGitFlowConfigAsync();
166178
});
167179
}
168180

@@ -217,7 +229,30 @@ public void RefreshBranches()
217229
}
218230
LocalBranchesCount = localBranchesCount;
219231

220-
UpdateGitFlowBranches(localBranches);
232+
// Check and auto-configure GitFlow if structure is detected
233+
CheckAndAutoConfigureGitFlow(localBranches);
234+
});
235+
236+
// Load GitFlow configuration after auto-configuration
237+
await LoadGitFlowConfigAsync().ConfigureAwait(false);
238+
239+
ExecuteOnUIThread(() =>
240+
{
241+
// Get local branches again for GitFlow update
242+
var localBranches = new List<Models.Branch>();
243+
foreach (var b in branches)
244+
{
245+
if (b.IsLocal && !b.IsDetachedHead)
246+
{
247+
localBranches.Add(b);
248+
}
249+
}
250+
251+
// Update GitFlow branches only if GitFlow display is enabled
252+
if (_settings != null && _settings.ShowGitFlowInSidebar)
253+
{
254+
UpdateGitFlowBranches(localBranches);
255+
}
221256
UpdateWorkingCopyRemotesInfo(remotes);
222257
UpdatePendingPullPushState();
223258
});
@@ -475,18 +510,20 @@ private void UpdateGitFlowBranches(List<Models.Branch> localBranches)
475510
{
476511
try
477512
{
478-
if (IsGitFlowEnabled())
513+
// Check if we should update GitFlow branches
514+
// Don't require IsGitFlowEnabled() here as it checks for branch existence which might not be loaded yet
515+
if (GitFlow != null && GitFlow.IsValid && _settings != null && _settings.ShowGitFlowInSidebar)
479516
{
480517
var groups = new List<Models.GitFlowBranchGroup>();
481518
var gitFlowBranches = new List<Models.Branch>();
482-
519+
483520
var featureGroup = new Models.GitFlowBranchGroup { Type = Models.GitFlowBranchType.Feature, Name = "Features" };
484521
var releaseGroup = new Models.GitFlowBranchGroup { Type = Models.GitFlowBranchType.Release, Name = "Releases" };
485522
var hotfixGroup = new Models.GitFlowBranchGroup { Type = Models.GitFlowBranchType.Hotfix, Name = "Hotfixes" };
486-
523+
487524
foreach (var branch in localBranches)
488525
{
489-
var type = GetGitFlowType(branch);
526+
var type = GetGitFlowTypeForBranch(branch);
490527
switch (type)
491528
{
492529
case Models.GitFlowBranchType.Feature:
@@ -503,14 +540,14 @@ private void UpdateGitFlowBranches(List<Models.Branch> localBranches)
503540
break;
504541
}
505542
}
506-
543+
507544
if (featureGroup.Branches.Count > 0)
508545
groups.Add(featureGroup);
509546
if (releaseGroup.Branches.Count > 0)
510547
groups.Add(releaseGroup);
511548
if (hotfixGroup.Branches.Count > 0)
512549
groups.Add(hotfixGroup);
513-
550+
514551
GitFlowBranchGroups = groups;
515552
GitFlowBranches = gitFlowBranches;
516553
}
@@ -528,6 +565,24 @@ private void UpdateGitFlowBranches(List<Models.Branch> localBranches)
528565
}
529566
}
530567

568+
private Models.GitFlowBranchType GetGitFlowTypeForBranch(Models.Branch b)
569+
{
570+
if (GitFlow == null || !GitFlow.IsValid)
571+
return Models.GitFlowBranchType.None;
572+
573+
var name = b.Name;
574+
if (name.StartsWith(GitFlow.FeaturePrefix, StringComparison.Ordinal))
575+
return Models.GitFlowBranchType.Feature;
576+
if (name.StartsWith(GitFlow.ReleasePrefix, StringComparison.Ordinal))
577+
return Models.GitFlowBranchType.Release;
578+
if (name.StartsWith(GitFlow.HotfixPrefix, StringComparison.Ordinal))
579+
return Models.GitFlowBranchType.Hotfix;
580+
if (!string.IsNullOrEmpty(GitFlow.SupportPrefix) && name.StartsWith(GitFlow.SupportPrefix, StringComparison.Ordinal))
581+
return Models.GitFlowBranchType.Support;
582+
583+
return Models.GitFlowBranchType.None;
584+
}
585+
531586
/// <summary>
532587
/// Updates working copy remotes information
533588
/// </summary>
@@ -601,18 +656,141 @@ private async Task LoadGitFlowConfigAsync()
601656
var config = await new Commands.Config(_fullpath).ReadAllAsync().ConfigureAwait(false);
602657
_hasAllowedSignersFile = config.TryGetValue("gpg.ssh.allowedSignersFile", out var allowedSignersFile) && !string.IsNullOrEmpty(allowedSignersFile);
603658

604-
if (config.TryGetValue("gitflow.branch.master", out var masterName))
605-
GitFlow.Master = masterName;
606-
if (config.TryGetValue("gitflow.branch.develop", out var developName))
607-
GitFlow.Develop = developName;
608-
if (config.TryGetValue("gitflow.prefix.feature", out var featurePrefix))
609-
GitFlow.FeaturePrefix = featurePrefix;
610-
if (config.TryGetValue("gitflow.prefix.release", out var releasePrefix))
611-
GitFlow.ReleasePrefix = releasePrefix;
612-
if (config.TryGetValue("gitflow.prefix.hotfix", out var hotfixPrefix))
613-
GitFlow.HotfixPrefix = hotfixPrefix;
659+
// Check if GitFlow is configured explicitly
660+
bool hasGitFlowConfig = config.Keys.Any(k => k.StartsWith("gitflow."));
661+
662+
if (hasGitFlowConfig)
663+
{
664+
// Load explicit GitFlow configuration
665+
if (config.TryGetValue("gitflow.branch.master", out var masterName))
666+
GitFlow.Master = masterName;
667+
if (config.TryGetValue("gitflow.branch.develop", out var developName))
668+
GitFlow.Develop = developName;
669+
if (config.TryGetValue("gitflow.prefix.feature", out var featurePrefix))
670+
GitFlow.FeaturePrefix = featurePrefix;
671+
if (config.TryGetValue("gitflow.prefix.release", out var releasePrefix))
672+
GitFlow.ReleasePrefix = releasePrefix;
673+
if (config.TryGetValue("gitflow.prefix.hotfix", out var hotfixPrefix))
674+
GitFlow.HotfixPrefix = hotfixPrefix;
675+
if (config.TryGetValue("gitflow.prefix.support", out var supportPrefix))
676+
GitFlow.SupportPrefix = supportPrefix;
677+
if (config.TryGetValue("gitflow.prefix.versiontag", out var versionTagPrefix))
678+
GitFlow.VersionTagPrefix = versionTagPrefix;
679+
}
680+
else if (_branches != null && _branches.Count > 0)
681+
{
682+
// Auto-detect GitFlow based on branch structure when no explicit config exists
683+
// Check for master or main branch
684+
var masterBranch = _branches.Find(b => b.IsLocal && b.Name == "master");
685+
var mainBranch = _branches.Find(b => b.IsLocal && b.Name == "main");
686+
687+
if (masterBranch != null || mainBranch != null)
688+
{
689+
// Set master/main branch
690+
GitFlow.Master = masterBranch != null ? "master" : "main";
691+
692+
// Check for develop branch
693+
var developBranch = _branches.Find(b => b.IsLocal && b.Name == "develop");
694+
695+
if (developBranch != null)
696+
{
697+
GitFlow.Develop = "develop";
698+
699+
// Auto-detect common GitFlow prefixes based on existing branches
700+
bool hasFeatureBranches = _branches.Any(b => b.IsLocal && b.Name.StartsWith("feature/"));
701+
bool hasReleaseBranches = _branches.Any(b => b.IsLocal && b.Name.StartsWith("release/"));
702+
bool hasHotfixBranches = _branches.Any(b => b.IsLocal && b.Name.StartsWith("hotfix/"));
703+
bool hasSupportBranches = _branches.Any(b => b.IsLocal && b.Name.StartsWith("support/"));
704+
705+
// Set default GitFlow prefixes
706+
GitFlow.FeaturePrefix = "feature/";
707+
GitFlow.ReleasePrefix = "release/";
708+
GitFlow.HotfixPrefix = "hotfix/";
709+
710+
if (hasSupportBranches)
711+
GitFlow.SupportPrefix = "support/";
712+
713+
GitFlow.VersionTagPrefix = "";
714+
715+
// Auto-initialize GitFlow configuration for the repository
716+
// This allows GitFlow commands to work without manual initialization
717+
Task.Run(async () =>
718+
{
719+
try
720+
{
721+
// Write GitFlow configuration synchronously to ensure it's set
722+
var configCmd = new Commands.Config(_fullpath);
723+
await configCmd.SetAsync("gitflow.branch.master", GitFlow.Master).ConfigureAwait(false);
724+
await configCmd.SetAsync("gitflow.branch.develop", GitFlow.Develop).ConfigureAwait(false);
725+
await configCmd.SetAsync("gitflow.prefix.feature", GitFlow.FeaturePrefix).ConfigureAwait(false);
726+
await configCmd.SetAsync("gitflow.prefix.release", GitFlow.ReleasePrefix).ConfigureAwait(false);
727+
await configCmd.SetAsync("gitflow.prefix.hotfix", GitFlow.HotfixPrefix).ConfigureAwait(false);
728+
await configCmd.SetAsync("gitflow.prefix.bugfix", "bugfix/").ConfigureAwait(false);
729+
await configCmd.SetAsync("gitflow.prefix.support", GitFlow.SupportPrefix).ConfigureAwait(false);
730+
await configCmd.SetAsync("gitflow.prefix.versiontag", GitFlow.VersionTagPrefix ?? "", true).ConfigureAwait(false);
731+
}
732+
catch (Exception ex)
733+
{
734+
// Silently fail - auto-configuration is optional
735+
App.LogException(ex);
736+
}
737+
});
738+
}
739+
}
740+
}
614741
}
615742

616743
#endregion
744+
745+
/// <summary>
746+
/// Checks for GitFlow structure and auto-configures if detected
747+
/// </summary>
748+
private void CheckAndAutoConfigureGitFlow(List<Models.Branch> localBranches)
749+
{
750+
// Check if GitFlow is already configured
751+
Task.Run(async () =>
752+
{
753+
var config = await new Commands.Config(_fullpath).ReadAllAsync().ConfigureAwait(false);
754+
bool hasGitFlowConfig = config.Keys.Any(k => k.StartsWith("gitflow."));
755+
756+
if (!hasGitFlowConfig && localBranches != null && localBranches.Count > 0)
757+
{
758+
// Check for master or main branch
759+
var masterBranch = localBranches.Find(b => b.Name == "master");
760+
var mainBranch = localBranches.Find(b => b.Name == "main");
761+
var developBranch = localBranches.Find(b => b.Name == "develop");
762+
763+
if ((masterBranch != null || mainBranch != null) && developBranch != null)
764+
{
765+
var primaryBranch = masterBranch != null ? "master" : "main";
766+
767+
// Write GitFlow configuration
768+
var configCmd = new Commands.Config(_fullpath);
769+
await configCmd.SetAsync("gitflow.branch.master", primaryBranch).ConfigureAwait(false);
770+
await configCmd.SetAsync("gitflow.branch.develop", "develop").ConfigureAwait(false);
771+
await configCmd.SetAsync("gitflow.prefix.feature", "feature/").ConfigureAwait(false);
772+
await configCmd.SetAsync("gitflow.prefix.release", "release/").ConfigureAwait(false);
773+
await configCmd.SetAsync("gitflow.prefix.hotfix", "hotfix/").ConfigureAwait(false);
774+
await configCmd.SetAsync("gitflow.prefix.bugfix", "bugfix/").ConfigureAwait(false);
775+
await configCmd.SetAsync("gitflow.prefix.support", "support/").ConfigureAwait(false);
776+
await configCmd.SetAsync("gitflow.prefix.versiontag", "", true).ConfigureAwait(false);
777+
778+
// Update the GitFlow object
779+
ExecuteOnUIThread(() =>
780+
{
781+
GitFlow.Master = primaryBranch;
782+
GitFlow.Develop = "develop";
783+
GitFlow.FeaturePrefix = "feature/";
784+
GitFlow.ReleasePrefix = "release/";
785+
GitFlow.HotfixPrefix = "hotfix/";
786+
GitFlow.SupportPrefix = "support/";
787+
GitFlow.VersionTagPrefix = "";
788+
});
789+
790+
System.Diagnostics.Debug.WriteLine($"[GitFlow] Auto-configured for {primaryBranch}/develop structure");
791+
}
792+
}
793+
});
794+
}
617795
}
618796
}

src/ViewModels/Repository.cs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,69 @@ public bool ShowGitFlowInSidebar
439439
{
440440
_settings.ShowGitFlowInSidebar = value;
441441
OnPropertyChanged();
442+
443+
// When enabling GitFlow display, check configuration and update branches
444+
if (value)
445+
{
446+
Task.Run(async () =>
447+
{
448+
// Load GitFlow configuration if not already loaded
449+
await LoadGitFlowConfigAsync();
450+
451+
// Ensure we have the latest branches
452+
if (_branches == null || _branches.Count == 0)
453+
{
454+
// Refresh branches if not loaded
455+
var branches = await new Commands.QueryBranches(_fullpath).GetResultAsync();
456+
if (branches != null)
457+
{
458+
_branches = branches;
459+
}
460+
}
461+
462+
// Collect local branches
463+
var localBranches = new List<Models.Branch>();
464+
if (_branches != null)
465+
{
466+
foreach (var b in _branches)
467+
{
468+
if (b.IsLocal && !b.IsDetachedHead)
469+
localBranches.Add(b);
470+
}
471+
}
472+
473+
// Update GitFlow branches if enabled
474+
// Note: We need to update on UI thread with the correct branches
475+
Dispatcher.UIThread.Invoke(() =>
476+
{
477+
// Re-check if GitFlow is enabled after loading config
478+
if (GitFlow != null && GitFlow.IsValid)
479+
{
480+
UpdateGitFlowBranches(localBranches);
481+
482+
// Auto-expand GitFlow section if it has content
483+
if (GitFlowBranchGroups != null && GitFlowBranchGroups.Count > 0)
484+
{
485+
_settings.IsGitFlowExpandedInSideBar = true;
486+
}
487+
488+
// Force UI to refresh GitFlow section completely
489+
OnPropertyChanged(nameof(GitFlowBranchGroups));
490+
OnPropertyChanged(nameof(GitFlowBranches));
491+
OnPropertyChanged(nameof(IsGitFlowGroupExpanded));
492+
493+
// Also ensure the sidebar knows to show GitFlow
494+
OnPropertyChanged(nameof(ShowGitFlowInSidebar));
495+
}
496+
});
497+
});
498+
}
499+
else
500+
{
501+
// Clear GitFlow branches when disabled
502+
GitFlowBranchGroups = new List<Models.GitFlowBranchGroup>();
503+
GitFlowBranches = new List<Models.Branch>();
504+
}
442505
}
443506
}
444507
}
@@ -718,7 +781,7 @@ public async Task ShowAndStartPopupAsync(Popup popup)
718781

719782
public bool IsGitFlowEnabled()
720783
{
721-
return GitFlow != null &&
784+
return GitFlow != null &&
722785
GitFlow.IsValid &&
723786
_branches != null &&
724787
_branches.Find(x => x.IsLocal && x.Name.Equals(GitFlow.Master, StringComparison.Ordinal)) != null &&

0 commit comments

Comments
 (0)