Skip to content

Add sorting by Status or Path to Local Changes / Stashes / History | Changes display modes dropdown #1613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions src/Models/Change.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ public enum ChangeViewMode
Tree,
}

public enum ChangeSortMode
{
Path,
Status,
}

public enum ChangeState
{
None,
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/de_DE.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Zeige als Datei- und Ordnerliste</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Zeige als Pfadliste</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Zeige als Dateisystembaum</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Nach Pfad sortieren</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Nach Status sortieren</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl" xml:space="preserve">Ändere URL des Submoduls</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.Submodule" xml:space="preserve">Submodule:</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.URL" xml:space="preserve">URL:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Show as File and Dir List</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Show as Path List</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Show as Filesystem Tree</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Sort by Path</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Sort by Status</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl" xml:space="preserve">Change Submodule's URL</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.Submodule" xml:space="preserve">Submodule:</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.URL" xml:space="preserve">URL:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/es_ES.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Mostrar como Lista de Archivos y Directorios</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Mostrar como Lista de Rutas</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Mostrar como Árbol de Sistema de Archivos</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Ordenar por ruta</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Ordenar por estado</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl" xml:space="preserve">Cambiar la URL del Submódulo</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.Submodule" xml:space="preserve">Submódulo:</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.URL" xml:space="preserve">URL:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/fr_FR.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Afficher comme liste de dossiers/fichiers</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Afficher comme liste de chemins</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Afficher comme arborescence</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Trier par chemin</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Trier par statut</x:String>
<x:String x:Key="Text.Checkout" xml:space="preserve">Récupérer la branche</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Récupérer ce commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit :</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/it_IT.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Mostra come elenco di file e cartelle</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Mostra come elenco di percorsi</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Mostra come albero del filesystem</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Ordina per percorso</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Ordina per stato</x:String>
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/ja_JP.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">ファイルとディレクトリのリストを表示</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">パスのリストを表示</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">ファイルシステムのツリーを表示</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">パスで並び替え</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">ステータスで並び替え</x:String>
<x:String x:Key="Text.Checkout" xml:space="preserve">ブランチをチェックアウト</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">コミットをチェックアウト</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">コミット:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/pt_BR.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Exibir como Lista de Arquivos e Diretórios</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Exibir como Lista de Caminhos</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Exibir como Árvore de Sistema de Arquivos</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Ordenar por caminho</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Ordenar por status</x:String>
<x:String x:Key="Text.Checkout" xml:space="preserve">Checkout Branch</x:String>
<x:String x:Key="Text.Checkout.Commit" xml:space="preserve">Checkout Commit</x:String>
<x:String x:Key="Text.Checkout.Commit.Target" xml:space="preserve">Commit:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/ru_RU.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">Показывать в виде списка файлов и каталогов</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">Показывать в виде списка путей</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">Показывать в виде дерева файловой системы</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">Сортировать по пути</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">Сортировать по статусу</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl" xml:space="preserve">Изменить URL-адрес подмодуля</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.Submodule" xml:space="preserve">Подмодуль:</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.URL" xml:space="preserve">URL-адрес:</x:String>
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/Locales/zh_CN.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
<x:String x:Key="Text.ChangeDisplayMode.Grid" xml:space="preserve">文件名+路径列表模式</x:String>
<x:String x:Key="Text.ChangeDisplayMode.List" xml:space="preserve">全路径列表模式</x:String>
<x:String x:Key="Text.ChangeDisplayMode.Tree" xml:space="preserve">文件目录树形结构模式</x:String>
<x:String x:Key="Text.ChangeSortMode.Path" xml:space="preserve">按路径排序</x:String>
<x:String x:Key="Text.ChangeSortMode.Status" xml:space="preserve">按状态排序</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl" xml:space="preserve">修改子模块远程地址</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.Submodule" xml:space="preserve">子模块 :</x:String>
<x:String x:Key="Text.ChangeSubmoduleUrl.URL" xml:space="preserve">远程地址 :</x:String>
Expand Down
80 changes: 72 additions & 8 deletions src/ViewModels/ChangeTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public ChangeTreeNode(string path, bool isExpanded, int depth)
IsExpanded = isExpanded;
}

public static List<ChangeTreeNode> Build(IList<Models.Change> changes, HashSet<string> folded)
public static List<ChangeTreeNode> Build(IList<Models.Change> changes, HashSet<string> folded, Models.ChangeSortMode sortMode = Models.ChangeSortMode.Path, bool isUnstagedContext = false)
{
var nodes = new List<ChangeTreeNode>();
var folders = new Dictionary<string, ChangeTreeNode>();
Expand Down Expand Up @@ -93,7 +93,7 @@ public static List<ChangeTreeNode> Build(IList<Models.Change> changes, HashSet<s
}
}

Sort(nodes);
Sort(nodes, sortMode, isUnstagedContext);

folders.Clear();
return nodes;
Expand All @@ -113,20 +113,84 @@ private static void InsertFolder(List<ChangeTreeNode> collection, ChangeTreeNode
collection.Add(subFolder);
}

private static void Sort(List<ChangeTreeNode> nodes)
private static void Sort(List<ChangeTreeNode> nodes, Models.ChangeSortMode sortMode, bool isUnstagedContext)
{
foreach (var node in nodes)
{
if (node.IsFolder)
Sort(node.Children);
Sort(node.Children, sortMode, isUnstagedContext);
}

nodes.Sort((l, r) =>
if (sortMode == Models.ChangeSortMode.Status)
{
if (l.IsFolder == r.IsFolder)
nodes.Sort((l, r) =>
{
// Sort folders first
if (l.IsFolder != r.IsFolder)
return l.IsFolder ? -1 : 1;

// If both are folders, sort by path
if (l.IsFolder && r.IsFolder)
return Models.NumericSort.Compare(l.FullPath, r.FullPath);

// For files, sort by status first
var leftPriority = GetStatusSortPriority(l.Change, isUnstagedContext);
var rightPriority = GetStatusSortPriority(r.Change, isUnstagedContext);

// First sort by status priority
var statusComparison = leftPriority.CompareTo(rightPriority);
if (statusComparison != 0)
return statusComparison;

// If status priorities are equal, sort by path as secondary sort
// Use the same sorting logic as the path-only sort for consistency
return Models.NumericSort.Compare(l.FullPath, r.FullPath);
return l.IsFolder ? -1 : 1;
});
});
}
else
{
nodes.Sort((l, r) =>
{
if (l.IsFolder == r.IsFolder)
return Models.NumericSort.Compare(l.FullPath, r.FullPath);
return l.IsFolder ? -1 : 1;
});
}
}

private static int GetStatusSortPriority(Models.Change change, bool isUnstagedContext)
{
if (change == null) return int.MaxValue;

if (isUnstagedContext)
{
// For unstaged context, only consider WorkTree state
return change.WorkTree switch
{
Models.ChangeState.Conflicted => 1, // Conflicts first - most urgent
Models.ChangeState.Modified => 2,
Models.ChangeState.TypeChanged => 3,
Models.ChangeState.Deleted => 4, // Missing files
Models.ChangeState.Renamed => 5,
Models.ChangeState.Copied => 6,
Models.ChangeState.Untracked => 7, // New files last
_ => 10
};
}
else
{
// For staged context, only consider Index state
return change.Index switch
{
Models.ChangeState.Modified => 1,
Models.ChangeState.TypeChanged => 2,
Models.ChangeState.Renamed => 3,
Models.ChangeState.Copied => 4,
Models.ChangeState.Added => 5,
Models.ChangeState.Deleted => 6,
_ => 10
};
}
}

private bool _isExpanded = true;
Expand Down
29 changes: 29 additions & 0 deletions src/ViewModels/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,30 @@ public Models.ChangeViewMode StashChangeViewMode
set => SetProperty(ref _stashChangeViewMode, value);
}

public Models.ChangeSortMode UnstagedChangeSortMode
{
get => _unstagedChangeSortMode;
set => SetProperty(ref _unstagedChangeSortMode, value);
}

public Models.ChangeSortMode StagedChangeSortMode
{
get => _stagedChangeSortMode;
set => SetProperty(ref _stagedChangeSortMode, value);
}

public Models.ChangeSortMode CommitChangeSortMode
{
get => _commitChangeSortMode;
set => SetProperty(ref _commitChangeSortMode, value);
}

public Models.ChangeSortMode StashChangeSortMode
{
get => _stashChangeSortMode;
set => SetProperty(ref _stashChangeSortMode, value);
}

public string GitInstallPath
{
get => Native.OS.GitExecutable;
Expand Down Expand Up @@ -715,6 +739,11 @@ private bool RemoveInvalidRepositoriesRecursive(List<RepositoryNode> collection)
private Models.ChangeViewMode _commitChangeViewMode = Models.ChangeViewMode.List;
private Models.ChangeViewMode _stashChangeViewMode = Models.ChangeViewMode.List;

private Models.ChangeSortMode _unstagedChangeSortMode = Models.ChangeSortMode.Path;
private Models.ChangeSortMode _stagedChangeSortMode = Models.ChangeSortMode.Path;
private Models.ChangeSortMode _commitChangeSortMode = Models.ChangeSortMode.Path;
private Models.ChangeSortMode _stashChangeSortMode = Models.ChangeSortMode.Path;

private string _gitDefaultCloneDir = string.Empty;

private int _shellOrTerminal = -1;
Expand Down
4 changes: 3 additions & 1 deletion src/Views/BranchCompare.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,14 @@
<v:ChangeViewModeSwitcher Grid.Column="1"
Width="14" Height="14"
HorizontalAlignment="Right"
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"
SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeSortMode, Mode=TwoWay}"/>
</Grid>

<!-- Changes -->
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
<v:ChangeCollectionView ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode}"
SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeSortMode}"
Changes="{Binding VisibleChanges}"
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
ContextRequested="OnChangeContextRequested"/>
Expand Down
Loading