private void UpdateSubmodulesList()
{
_previousUpdateTime = DateTime.Now;
// Cancel any previous async activities:
_submodulesStatusCTS.Cancel();
_submodulesStatusCTS.Dispose();
_submodulesStatusCTS = new CancellationTokenSource();
RemoveSubmoduleButtons();
toolStripButtonLevelUp.DropDownItems.Add(_loading.Text);
// Start gathering new submodule information asynchronously. This makes a significant difference in UI
// responsiveness if there are numerous submodules (e.g. > 100).
var cancelToken = _submodulesStatusCTS.Token;
string thisModuleDir = Module.WorkingDir;
// First task: Gather list of submodules on a background thread.
var updateTask = Task.Factory.StartNew(() =>
{
// Don't access Module directly because it's not thread-safe. Use a thread-local version:
GitModule threadModule = new GitModule(thisModuleDir);
SubmoduleInfoResult result = new SubmoduleInfoResult();
// Add all submodules inside the current repository:
foreach (var submodule in threadModule.GetSubmodulesLocalPaths().OrderBy(submoduleName => submoduleName))
{
cancelToken.ThrowIfCancellationRequested();
var name = submodule;
string path = threadModule.GetSubmoduleFullPath(submodule);
if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
name = name + " " + GetModuleBranch(path);
var smi = new SubmoduleInfo { Text = name, Path = path };
result.OurSubmodules.Add(smi);
GetSubmoduleStatusAsync(smi, cancelToken);
}
if (threadModule.SuperprojectModule != null)
{
GitModule supersuperproject = threadModule.FindTopProjectModule();
if (threadModule.SuperprojectModule.WorkingDir != supersuperproject.WorkingDir)
{
var name = Path.GetFileName(Path.GetDirectoryName(supersuperproject.WorkingDir));
string path = supersuperproject.WorkingDir;
if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
name = name + " " + GetModuleBranch(path);
result.TopProject = new SubmoduleInfo { Text = name, Path = supersuperproject.WorkingDir };
GetSubmoduleStatusAsync(result.TopProject, cancelToken);
}
{
string name;
GitModule parentModule = threadModule.SuperprojectModule;
string localpath = "";
if (threadModule.SuperprojectModule.WorkingDir != supersuperproject.WorkingDir)
{
parentModule = supersuperproject;
localpath = threadModule.SuperprojectModule.WorkingDir.Substring(supersuperproject.WorkingDir.Length);
localpath = PathUtil.GetDirectoryName(localpath.ToPosixPath());
name = localpath;
}
else
name = Path.GetFileName(Path.GetDirectoryName(supersuperproject.WorkingDir));
string path = threadModule.SuperprojectModule.WorkingDir;
if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
name = name + " " + GetModuleBranch(path);
result.Superproject = new SubmoduleInfo { Text = name, Path = threadModule.SuperprojectModule.WorkingDir };
GetSubmoduleStatusAsync(result.Superproject, cancelToken);
}
var submodules = supersuperproject.GetSubmodulesLocalPaths().OrderBy(submoduleName => submoduleName);
if (submodules.Any())
{
string localpath = threadModule.WorkingDir.Substring(supersuperproject.WorkingDir.Length);
localpath = PathUtil.GetDirectoryName(localpath.ToPosixPath());
foreach (var submodule in submodules)
{
cancelToken.ThrowIfCancellationRequested();
var name = submodule;
string path = supersuperproject.GetSubmoduleFullPath(submodule);
if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
name = name + " " + GetModuleBranch(path);
bool bold = false;
if (submodule == localpath)
{
result.CurrentSubmoduleName = threadModule.GetCurrentSubmoduleLocalPath();
bold = true;
}
var smi = new SubmoduleInfo { Text = name, Path = path, Bold = bold };
result.SuperSubmodules.Add(smi);
GetSubmoduleStatusAsync(smi, cancelToken);
}
}
}
return result;
}, cancelToken);
// Second task: Populate toolbar menu on UI thread. Note further tasks are created by
// CreateSubmoduleMenuItem to update images with submodule status.
updateTask.ContinueWith((task) =>
{
if (task.Result == null)
return;
RemoveSubmoduleButtons();
var newItems = new List<ToolStripItem>();
task.Result.OurSubmodules.ForEach(submodule => newItems.Add(CreateSubmoduleMenuItem(submodule)));
if (task.Result.OurSubmodules.Count == 0)
newItems.Add(new ToolStripMenuItem(_noSubmodulesPresent.Text));
if (task.Result.Superproject != null)
{
newItems.Add(new ToolStripSeparator());
if (task.Result.TopProject != null)
newItems.Add(CreateSubmoduleMenuItem(task.Result.TopProject, _topProjectModuleFormat.Text));
newItems.Add(CreateSubmoduleMenuItem(task.Result.Superproject, _superprojectModuleFormat.Text));
task.Result.SuperSubmodules.ForEach(submodule => newItems.Add(CreateSubmoduleMenuItem(submodule)));
}
newItems.Add(new ToolStripSeparator());
var mi = new ToolStripMenuItem(updateAllSubmodulesToolStripMenuItem.Text);
mi.Click += UpdateAllSubmodulesToolStripMenuItemClick;
newItems.Add(mi);
if (task.Result.CurrentSubmoduleName != null)
{
var usmi = new ToolStripMenuItem(_updateCurrentSubmodule.Text);
usmi.Tag = task.Result.CurrentSubmoduleName;
usmi.Click += UpdateSubmoduleToolStripMenuItemClick;
newItems.Add(usmi);
}
// Using AddRange is critical: if you used Add to add menu items one at a
// time, performance would be extremely slow with many submodules (> 100).
toolStripButtonLevelUp.DropDownItems.AddRange(newItems.ToArray());
_previousUpdateTime = DateTime.Now;
},
cancelToken,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
}