From a041d1cf67333986be5e8759be0f00f3fa376d32 Mon Sep 17 00:00:00 2001 From: "gabriele.palaj" Date: Thu, 14 May 2026 16:29:56 +0200 Subject: [PATCH 1/6] enhance: add checkboxes to delete worktree on branch delete and rename remote on branch rename; show repo name in toolbar --- src/Resources/Locales/de_DE.axaml | 2 ++ src/Resources/Locales/en_US.axaml | 2 ++ src/Resources/Locales/es_ES.axaml | 2 ++ src/Resources/Locales/fr_FR.axaml | 2 ++ src/Resources/Locales/id_ID.axaml | 2 ++ src/Resources/Locales/it_IT.axaml | 2 ++ src/Resources/Locales/ja_JP.axaml | 2 ++ src/Resources/Locales/ko_KR.axaml | 2 ++ src/Resources/Locales/pt_BR.axaml | 2 ++ src/Resources/Locales/ru_RU.axaml | 2 ++ src/Resources/Locales/ta_IN.axaml | 2 ++ src/Resources/Locales/uk_UA.axaml | 2 ++ src/Resources/Locales/zh_CN.axaml | 2 ++ src/Resources/Locales/zh_TW.axaml | 2 ++ src/ViewModels/DeleteBranch.cs | 37 +++++++++++++++++++++++++++ src/ViewModels/RenameBranch.cs | 42 +++++++++++++++++++++++++++++-- src/ViewModels/Repository.cs | 2 ++ src/Views/DeleteBranch.axaml | 9 ++++++- src/Views/RenameBranch.axaml | 9 ++++++- src/Views/RepositoryToolbar.axaml | 6 +++++ 20 files changed, 129 insertions(+), 4 deletions(-) diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index d4ecd50b9..ddc60e6f4 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -324,6 +324,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Branch: Du bist dabei, einen Remote-Branch zu löschen!!! Auch Remote-Branch ${0}$ löschen + Auch Worktree unter ${0}$ entfernen Mehrere Branches löschen Du versuchst, mehrere Branches auf einmal zu löschen. Kontrolliere dies sorgfältig, bevor du fortfährst! Mehrere Tags löschen @@ -719,6 +720,7 @@ $1, $2, … Werte der Eingabe-Steuerelemente Neuer Name: Eindeutiger Name für diesen Branch Branch: + Auch Remote-Branch ${0}$ umbenennen ABBRECHEN Änderungen automatisch von Remote fetchen… Sortieren diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 5d5b4a90b..6a5f14bac 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -338,6 +338,7 @@ Branch: You are about to delete a remote branch!!! Also delete remote branch ${0}$ + Also remove worktree at ${0}$ Delete Multiple Branches You are trying to delete multiple branches at one time. Be sure to double-check before taking action! Delete Multiple Tags @@ -748,6 +749,7 @@ New Name: Unique name for this branch Branch: + Also rename remote branch ${0}$ ABORT Auto fetching changes from remotes... Sort diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 6bcce5124..b9e13c18c 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -341,6 +341,7 @@ Rama: ¡Estás a punto de eliminar una rama remota! También eliminar la rama remota ${0}$ + También eliminar el worktree en ${0}$ Eliminar Múltiples Ramas Estás intentando eliminar múltiples ramas a la vez. ¡Asegúrate de comprobar dos veces antes de realizar esta acción! Eliminar Múltiples Etiquetas @@ -750,6 +751,7 @@ Nuevo Nombre: Nombre único para esta rama Rama: + Renombrar también la rama remota ${0}$ ABORTAR Auto fetching cambios desde remotos... Ordenar diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index f2d002964..cf6ad1664 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -311,6 +311,7 @@ Branche : Vous êtes sur le point de supprimer une branche distante !!! Supprimer également la branche distante ${0}$ + Supprimer également le worktree à ${0}$ Supprimer plusieurs branches Vous essayez de supprimer plusieurs branches à la fois. Assurez-vous de revérifier avant de procéder ! Supprimer plusieurs tags @@ -672,6 +673,7 @@ Nouveau nom : Nom unique pour cette branche Branche : + Renommer également la branche distante ${0}$ ABORT Fetch automatique des changements depuis les dépôts... Trier diff --git a/src/Resources/Locales/id_ID.axaml b/src/Resources/Locales/id_ID.axaml index 621abaa9c..ab01f76f3 100644 --- a/src/Resources/Locales/id_ID.axaml +++ b/src/Resources/Locales/id_ID.axaml @@ -295,6 +295,7 @@ Branch: Anda akan menghapus remote branch!!! Juga hapus remote branch ${0}$ + Juga hapus worktree di ${0}$ Hapus Beberapa Branch Anda akan menghapus beberapa branch sekaligus. Pastikan untuk memeriksa ulang sebelum bertindak! Hapus Beberapa Tag @@ -644,6 +645,7 @@ Nama Baru: Nama unik untuk branch ini Branch: + Juga ganti nama remote branch ${0}$ BATALKAN Auto fetch perubahan dari remote... Urut diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index 629f6da4d..7bfee1846 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -323,6 +323,7 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Branch: Stai per eliminare un branch remoto!!! Elimina anche il branch remoto ${0}$ + Rimuovi anche il worktree a ${0}$ Elimina Branch Multipli Stai per eliminare più branch contemporaneamente. Controlla attentamente prima di procedere! Elimina Tag Multipli @@ -715,6 +716,7 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Nuovo Nome: Nome univoco per questo branch Branch: + Rinomina anche il branch remoto ${0}$ ANNULLA Recupero automatico delle modifiche dai remoti... Ordina diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 26c1701bf..b4f7e563f 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -324,6 +324,7 @@ ブランチ: リモートブランチを削除しようとしています!!! リモートブランチの ${0}$ も削除 + ワークツリー ${0}$ も削除 複数のブランチを削除 複数のブランチをまとめて削除しようとしています。操作を行う前によく確認してください! 複数のタグを削除 @@ -721,6 +722,7 @@ 新しい名前: このブランチに付ける一意な名前 ブランチ: + リモートブランチ ${0}$ もリネーム 中止 リモートから変更を自動取得しています... 並べ替え diff --git a/src/Resources/Locales/ko_KR.axaml b/src/Resources/Locales/ko_KR.axaml index fa71db599..87ea04ef4 100644 --- a/src/Resources/Locales/ko_KR.axaml +++ b/src/Resources/Locales/ko_KR.axaml @@ -294,6 +294,7 @@ 브랜치: 원격 브랜치를 삭제하려고 합니다!!! 원격 브랜치 ${0}$도 함께 삭제 + 워크트리 ${0}$도 함께 제거 여러 브랜치 삭제 한 번에 여러 브랜치를 삭제하려고 합니다. 실행하기 전에 다시 한번 확인하세요! 여러 태그 삭제 @@ -645,6 +646,7 @@ 새 이름: 이 브랜치의 고유한 이름 브랜치: + 원격 브랜치 ${0}$도 함께 이름 변경 중단 원격에서 변경 사항 자동 Fetch 중... 정렬 diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 8c2f11205..6b894197e 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -223,6 +223,7 @@ Branch: Você está prestes a excluir uma branch remota!!! Também excluir branch remoto ${0}$ + Também remover worktree em ${0}$ Excluir Múltiplos Branches Você está tentando excluir vários branches de uma vez. Certifique-se de verificar antes de agir! Excluir Remoto @@ -511,6 +512,7 @@ Novo Nome: Nome único para este branch Branch: + Também renomear o branch remoto ${0}$ ABORTAR Buscando automaticamente mudanças dos remotos... Limpar (GC & Podar) diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 570a3bf2e..9f2364646 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -341,6 +341,7 @@ Ветка: Вы собираетесь удалить внешнюю ветку!!! Также удалите внешнюю ветку ${0}$ + Также удалить рабочую директорию ${0}$ Удаление нескольких веток Вы пытаетесь удалить несколько веток одновременно. Обязательно перепроверьте, прежде чем предпринимать какие-либо действия! Удалить несколько меток @@ -751,6 +752,7 @@ Новое имя: Уникальное имя для данной ветки Ветка: + Также переименовать удаленную ветку ${0}$ Отказ Автоматическое извлечение изменений с внешних репозиторий... Сортировать diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index 1dea63e92..a2e52deb9 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -203,6 +203,7 @@ கிளை: நீங்கள் ஒரு தொலை கிளையை நீக்கப் போகிறீர்கள்!!! தொலை ${0}$ கிளையையும் நீக்கு + ${0}$ வேலை மரத்தையும் அகற்று பல கிளைகளை நீக்கு நீங்கள் ஒரே நேரத்தில் பல கிளைகளை நீக்க முயற்சிக்கிறீர்கள் நடவடிக்கை எடுப்பதற்கு முன் மீண்டும் சரிபார்! தொலையை நீக்கு @@ -513,6 +514,7 @@ புதிய பெயர்: இந்தக் கிளைக்கான தனித்துவமான பெயர் கிளை: + தொலை ${0}$ கிளையின் பெயரையும் மாற்று நிறுத்து தொலைகளிலிருந்து மாற்றங்களைத் தானாகப் பெறுதல்... சுத்தப்படுத்தல்(சீசி & கத்தரித்தல்) diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index e21cc44c9..96f435122 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -207,6 +207,7 @@ Гілка: Ви збираєтеся видалити віддалену гілку!!! Також видалити віддалену гілку ${0}$ + Також видалити робочу директорію ${0}$ Видалити кілька гілок Ви намагаєтеся видалити кілька гілок одночасно. Перевірте ще раз перед виконанням! Видалити віддалене сховище @@ -517,6 +518,7 @@ Нова назва: Унікальна назва для цієї гілки Гілка: + Також перейменувати віддалену гілку ${0}$ ПЕРЕРВАТИ Автоматичне отримання змін з віддалених сховищ... Очистка (GC & Prune) diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 528596e0f..14d4fcaa9 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -342,6 +342,7 @@ 分支名 : 您正在删除远程上的分支,请务必小心!!! 同时删除远程分支 ${0}$ + 同时删除工作树 ${0}$ 删除多个分支 您正在尝试一次性删除多个分支,请务必仔细检查后再执行操作! 删除多个标签 @@ -752,6 +753,7 @@ 新的名称 : 新的分支名不能与现有分支名相同 分支 : + 同时重命名远程分支 ${0}$ 终止合并 自动拉取远端变更中... 排序方式 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 0142bb9b6..c8b76c259 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -342,6 +342,7 @@ 分支名稱: 您正在刪除遠端上的分支,請務必小心! 同時刪除遠端分支 ${0}$ + 同時刪除工作樹 ${0}$ 刪除多個分支 您正在嘗試一次性刪除多個分支,請務必仔細檢查後再刪除! 刪除多個標籤 @@ -752,6 +753,7 @@ 新名稱: 新的分支名稱不能與現有分支名稱相同 分支: + 同時重新命名遠端分支 ${0}$ 中止 自動提取遠端變更中... 排序 diff --git a/src/ViewModels/DeleteBranch.cs b/src/ViewModels/DeleteBranch.cs index 8d8821079..66a588e0c 100644 --- a/src/ViewModels/DeleteBranch.cs +++ b/src/ViewModels/DeleteBranch.cs @@ -26,6 +26,24 @@ public bool AlsoDeleteTrackingRemote set => SetProperty(ref _alsoDeleteTrackingRemote, value); } + public string DeleteWorktreeTip + { + get; + private set; + } + + public bool AlsoRemoveWorktree + { + get => _alsoRemoveWorktree; + set => SetProperty(ref _alsoRemoveWorktree, value); + } + + public bool HasWorktree + { + get; + private set; + } + public DeleteBranch(Repository repo, Models.Branch branch) { _repo = repo; @@ -37,6 +55,12 @@ public DeleteBranch(Repository repo, Models.Branch branch) if (TrackingRemoteBranch != null) DeleteTrackingRemoteTip = App.Text("DeleteBranch.WithTrackingRemote", TrackingRemoteBranch.FriendlyName); } + + if (branch.HasWorktree) + { + HasWorktree = true; + DeleteWorktreeTip = App.Text("DeleteBranch.WithWorktree", branch.WorktreePath); + } } public override async Task Sure() @@ -49,6 +73,8 @@ public override async Task Sure() if (Target.IsLocal) { + var worktreePath = Target.WorktreePath; + await new Commands.Branch(_repo.FullPath, Target.Name) .Use(log) .DeleteLocalAsync(); @@ -59,6 +85,16 @@ public override async Task Sure() await DeleteRemoteBranchAsync(TrackingRemoteBranch, log); _repo.UIStates.RemoveHistoryFilter(TrackingRemoteBranch.FullName, Models.FilterType.RemoteBranch); } + + if (_alsoRemoveWorktree && !string.IsNullOrEmpty(worktreePath)) + { + await new Commands.Worktree(_repo.FullPath) + .Use(log) + .RemoveAsync(worktreePath, false); + await new Commands.Worktree(_repo.FullPath) + .Use(log) + .PruneAsync(); + } } else { @@ -91,5 +127,6 @@ private async Task DeleteRemoteBranchAsync(Models.Branch branch, CommandLog log) private readonly Repository _repo = null; private bool _alsoDeleteTrackingRemote = false; + private bool _alsoRemoveWorktree = false; } } diff --git a/src/ViewModels/RenameBranch.cs b/src/ViewModels/RenameBranch.cs index dbca651e7..3e66d420f 100644 --- a/src/ViewModels/RenameBranch.cs +++ b/src/ViewModels/RenameBranch.cs @@ -11,6 +11,23 @@ public Models.Branch Target get; } + public Models.Branch TrackingRemoteBranch + { + get; + } + + public string RenameRemoteTip + { + get; + private set; + } + + public bool AlsoRenameRemote + { + get => _alsoRenameRemote; + set => SetProperty(ref _alsoRenameRemote, value); + } + [Required(ErrorMessage = "Branch name is required!!!")] [RegularExpression(@"^[\w\-/\.#\+]+$", ErrorMessage = "Bad branch name format!")] [CustomValidation(typeof(RenameBranch), nameof(ValidateBranchName))] @@ -25,6 +42,13 @@ public RenameBranch(Repository repo, Models.Branch target) _repo = repo; _name = target.Name; Target = target; + + if (target.IsLocal && !string.IsNullOrEmpty(target.Upstream)) + { + TrackingRemoteBranch = repo.Branches.Find(x => x.FullName == target.Upstream); + if (TrackingRemoteBranch != null) + RenameRemoteTip = App.Text("RenameBranch.WithTrackingRemote", TrackingRemoteBranch.FriendlyName); + } } public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) @@ -52,21 +76,35 @@ public override async Task Sure() var log = _repo.CreateLog($"Rename Branch '{Target.Name}'"); Use(log); - var isCurrent = Target.IsCurrent; - var oldName = Target.FullName; + var oldName = Target.Name; var succ = await new Commands.Branch(_repo.FullPath, Target.Name) .Use(log) .RenameAsync(_name); if (succ) + { _repo.RefreshAfterRenameBranch(Target, _name); + if (_alsoRenameRemote && TrackingRemoteBranch != null) + { + var remote = TrackingRemoteBranch.Remote; + var pushNew = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{_name}", false); + pushNew.Use(log); + await pushNew.RunAsync(); + + var deleteOld = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{oldName}", true); + deleteOld.Use(log); + await deleteOld.RunAsync(); + } + } + log.Complete(); return succ; } private readonly Repository _repo; private string _name; + private bool _alsoRenameRemote = false; } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index e9c573bf7..b3a2f2928 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -24,6 +24,8 @@ public string FullPath get; } + public string RepoName => System.IO.Path.GetFileName(FullPath); + public string GitDir { get; diff --git a/src/Views/DeleteBranch.axaml b/src/Views/DeleteBranch.axaml index a2640799a..ae29ebfed 100644 --- a/src/Views/DeleteBranch.axaml +++ b/src/Views/DeleteBranch.axaml @@ -18,7 +18,7 @@ Text="{DynamicResource Text.DeleteBranch}"/> - + @@ -50,6 +50,13 @@ + + + + + + diff --git a/src/Views/RenameBranch.axaml b/src/Views/RenameBranch.axaml index bcc405346..fc7fc1002 100644 --- a/src/Views/RenameBranch.axaml +++ b/src/Views/RenameBranch.axaml @@ -18,7 +18,7 @@ Text="{DynamicResource Text.RenameBranch}"/> - + + + + + + + diff --git a/src/Views/RepositoryToolbar.axaml b/src/Views/RepositoryToolbar.axaml index 72ebb9dc5..1685eb8ed 100644 --- a/src/Views/RepositoryToolbar.axaml +++ b/src/Views/RepositoryToolbar.axaml @@ -108,6 +108,12 @@ + + From 4ee5cffe7b06087cb15f06a34f9d5a047d7d08c1 Mon Sep 17 00:00:00 2001 From: "gabriele.palaj" Date: Thu, 14 May 2026 16:50:58 +0200 Subject: [PATCH 2/6] fix: use force when removing worktree on branch delete to handle non-empty directories --- .gitignore | 1 + src/ViewModels/DeleteBranch.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a0576b9fc..c1c7bad63 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ ehthumbs_vista.db bin/ obj/ +publish/ # ignore ci node files node_modules/ package.json diff --git a/src/ViewModels/DeleteBranch.cs b/src/ViewModels/DeleteBranch.cs index 66a588e0c..cc88a6c6d 100644 --- a/src/ViewModels/DeleteBranch.cs +++ b/src/ViewModels/DeleteBranch.cs @@ -90,7 +90,7 @@ public override async Task Sure() { await new Commands.Worktree(_repo.FullPath) .Use(log) - .RemoveAsync(worktreePath, false); + .RemoveAsync(worktreePath, true); await new Commands.Worktree(_repo.FullPath) .Use(log) .PruneAsync(); From 9f25d3a0fab939662b6cd148eaa42e69f4621427 Mon Sep 17 00:00:00 2001 From: "gabriele.palaj" Date: Thu, 14 May 2026 18:03:13 +0200 Subject: [PATCH 3/6] fix: remove worktree before deleting branch to avoid git rejection of checked-out branch --- src/ViewModels/DeleteBranch.cs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/ViewModels/DeleteBranch.cs b/src/ViewModels/DeleteBranch.cs index cc88a6c6d..39c7d45f8 100644 --- a/src/ViewModels/DeleteBranch.cs +++ b/src/ViewModels/DeleteBranch.cs @@ -75,6 +75,18 @@ public override async Task Sure() { var worktreePath = Target.WorktreePath; + // Remove the worktree first, since git refuses to delete a branch + // that is checked out in another worktree. + if (_alsoRemoveWorktree && !string.IsNullOrEmpty(worktreePath)) + { + await new Commands.Worktree(_repo.FullPath) + .Use(log) + .RemoveAsync(worktreePath, true); + await new Commands.Worktree(_repo.FullPath) + .Use(log) + .PruneAsync(); + } + await new Commands.Branch(_repo.FullPath, Target.Name) .Use(log) .DeleteLocalAsync(); @@ -85,16 +97,6 @@ public override async Task Sure() await DeleteRemoteBranchAsync(TrackingRemoteBranch, log); _repo.UIStates.RemoveHistoryFilter(TrackingRemoteBranch.FullName, Models.FilterType.RemoteBranch); } - - if (_alsoRemoveWorktree && !string.IsNullOrEmpty(worktreePath)) - { - await new Commands.Worktree(_repo.FullPath) - .Use(log) - .RemoveAsync(worktreePath, true); - await new Commands.Worktree(_repo.FullPath) - .Use(log) - .PruneAsync(); - } } else { From 32e91a94497f25a141b72d45edb64b5ca423056b Mon Sep 17 00:00:00 2001 From: "gabriele.palaj" Date: Thu, 14 May 2026 18:15:27 +0200 Subject: [PATCH 4/6] enhance: add Branch and Worktree toolbar buttons with text labels --- src/Resources/Locales/en_US.axaml | 16 ++++----- src/Views/RepositoryToolbar.axaml | 49 ++++++++++++++++++++++------ src/Views/RepositoryToolbar.axaml.cs | 18 ++++++++++ 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 6a5f14bac..2138acfca 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -8,7 +8,7 @@ Add File(s) To Ignore Pattern: Storage File: - Add Worktree + Worktree Location: Path for this worktree. Relative path is supported. Branch Name: @@ -205,8 +205,8 @@ Compare with HEAD Repository Configure COMMIT TEMPLATE - Built-in parameters: - + Built-in parameters: + ${branch_name} Current local branch name. ${files_num} Number of changed files ${files} Paths of changed files @@ -217,8 +217,8 @@ Template Name: CUSTOM ACTION Arguments: - Built-in parameters: - + Built-in parameters: + ${REPO} Repository's path ${REMOTE} Selected remote or selected branch's remote ${BRANCH} Selected branch, without ${REMOTE} part for remote branches @@ -306,7 +306,7 @@ Copy All Text Copy Full Path Copy Path - Create Branch... + Branch... Based On: Check out the created branch Local Changes: @@ -780,7 +780,7 @@ LOCAL BRANCHES More options... Navigate to HEAD - Create Branch + Branch CLEAR NOTIFICATIONS Only highlight current branch Open as Folder @@ -818,7 +818,7 @@ View Logs Visit '{0}' in Browser WORKTREES - Add Worktree + Worktree Prune Git Repository URL Reset Current Branch To Revision diff --git a/src/Views/RepositoryToolbar.axaml b/src/Views/RepositoryToolbar.axaml index 1685eb8ed..08d631b57 100644 --- a/src/Views/RepositoryToolbar.axaml +++ b/src/Views/RepositoryToolbar.axaml @@ -27,7 +27,7 @@ - - - - - + + + + Date: Fri, 15 May 2026 08:17:32 +0200 Subject: [PATCH 5/6] enhance: add rename remote branch popup and remove tracking reference menu item --- src/Resources/Locales/en_US.axaml | 2 + src/ViewModels/RenameRemoteBranch.cs | 64 +++++++++++++++++++++++++++ src/Views/BranchTree.axaml.cs | 32 ++++++++++++++ src/Views/RenameRemoteBranch.axaml | 43 ++++++++++++++++++ src/Views/RenameRemoteBranch.axaml.cs | 12 +++++ 5 files changed, 153 insertions(+) create mode 100644 src/ViewModels/RenameRemoteBranch.cs create mode 100644 src/Views/RenameRemoteBranch.axaml create mode 100644 src/Views/RenameRemoteBranch.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 2138acfca..9667dcea7 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -82,6 +82,7 @@ Push ${0}$ Rebase ${0}$ on ${1}$... Rename ${0}$... + Remove Tracking Reference Reset ${0}$ to ${1}$... Switch to ${0}$ (worktree) Set Tracking Branch... @@ -750,6 +751,7 @@ Unique name for this branch Branch: Also rename remote branch ${0}$ + Rename Remote Branch ABORT Auto fetching changes from remotes... Sort diff --git a/src/ViewModels/RenameRemoteBranch.cs b/src/ViewModels/RenameRemoteBranch.cs new file mode 100644 index 000000000..7d2f36d0e --- /dev/null +++ b/src/ViewModels/RenameRemoteBranch.cs @@ -0,0 +1,64 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class RenameRemoteBranch : Popup + { + public Models.Branch Target + { + get; + } + + [Required(ErrorMessage = "Branch name is required!!!")] + [RegularExpression(@"^[\w\-/\.#\+]+$", ErrorMessage = "Bad branch name format!")] + public string Name + { + get => _name; + set => SetProperty(ref _name, value, true); + } + + public RenameRemoteBranch(Repository repo, Models.Branch target) + { + _repo = repo; + Target = target; + _name = target.Name; + } + + public override async Task Sure() + { + if (Target.Name.Equals(_name, StringComparison.Ordinal)) + return true; + + using var lockWatcher = _repo.LockWatcher(); + ProgressDescription = $"Rename remote branch '{Target.FriendlyName}'"; + + var log = _repo.CreateLog($"Rename Remote Branch '{Target.FriendlyName}'"); + Use(log); + + var remote = Target.Remote; + var succ = false; + + // Push the current commit to the new branch name on the remote + var pushNew = new Commands.Push(_repo.FullPath, Target.Head, remote, $"refs/heads/{_name}", false, false, false, false); + pushNew.Use(log); + var pushOk = await pushNew.RunAsync(); + + if (pushOk) + { + // Delete the old remote branch + var deleteOld = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{Target.Name}", true); + deleteOld.Use(log); + succ = await deleteOld.RunAsync(); + } + + log.Complete(); + _repo.MarkBranchesDirtyManually(); + return succ; + } + + private readonly Repository _repo; + private string _name; + } +} diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 02f40085c..5593ad327 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -992,6 +992,27 @@ private ContextMenu CreateContextMenuForLocalBranch(ViewModels.Repository repo, e.Handled = true; }; menu.Items.Add(tracking); + + if (!string.IsNullOrEmpty(branch.Upstream)) + { + var removeTracking = new MenuItem(); + removeTracking.Header = App.Text("BranchCM.RemoveTracking"); + removeTracking.Icon = this.CreateMenuIcon("Icons.Clear"); + removeTracking.Click += async (_, e) => + { + if (repo.CanCreatePopup()) + { + var log = repo.CreateLog("Remove Tracking Reference"); + await new Commands.Branch(repo.FullPath, branch.Name) + .Use(log) + .SetUpstreamAsync(null); + log.Complete(); + repo.MarkBranchesDirtyManually(); + } + e.Handled = true; + }; + menu.Items.Add(removeTracking); + } } } @@ -1203,6 +1224,17 @@ public ContextMenu CreateContextMenuForRemoteBranch(ViewModels.Repository repo, menu.Items.Add(new MenuItem() { Header = "-" }); + var rename = new MenuItem(); + rename.Header = App.Text("BranchCM.Rename", name); + rename.Icon = this.CreateMenuIcon("Icons.Rename"); + rename.Click += (_, e) => + { + if (repo.CanCreatePopup()) + repo.ShowPopup(new ViewModels.RenameRemoteBranch(repo, branch)); + e.Handled = true; + }; + menu.Items.Add(rename); + var editDescription = new MenuItem(); editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name); editDescription.Icon = this.CreateMenuIcon("Icons.Edit"); diff --git a/src/Views/RenameRemoteBranch.axaml b/src/Views/RenameRemoteBranch.axaml new file mode 100644 index 000000000..75b9d90c2 --- /dev/null +++ b/src/Views/RenameRemoteBranch.axaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/RenameRemoteBranch.axaml.cs b/src/Views/RenameRemoteBranch.axaml.cs new file mode 100644 index 000000000..99bad41f9 --- /dev/null +++ b/src/Views/RenameRemoteBranch.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class RenameRemoteBranch : UserControl + { + public RenameRemoteBranch() + { + InitializeComponent(); + } + } +} From 73ec30191865e0add6727b56524231e7980c91a5 Mon Sep 17 00:00:00 2001 From: "gabriele.palaj" Date: Fri, 15 May 2026 08:41:06 +0200 Subject: [PATCH 6/6] fix: update local branch tracking on remote branch rename/delete and add tracking change guard --- src/ViewModels/DeleteBranch.cs | 12 ++++++++++++ src/ViewModels/RenameBranch.cs | 25 +++++++++++++++++++++++-- src/ViewModels/RenameRemoteBranch.cs | 23 +++++++++++++++++++++++ src/Views/RenameBranch.axaml | 2 +- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/ViewModels/DeleteBranch.cs b/src/ViewModels/DeleteBranch.cs index 39c7d45f8..2c7ebf6c6 100644 --- a/src/ViewModels/DeleteBranch.cs +++ b/src/ViewModels/DeleteBranch.cs @@ -100,8 +100,20 @@ public override async Task Sure() } else { + var oldUpstream = Target.FullName; await DeleteRemoteBranchAsync(Target, log); _repo.UIStates.RemoveHistoryFilter(Target.FullName, Models.FilterType.RemoteBranch); + + // Unset tracking on any local branches that were tracking this remote branch + foreach (var local in _repo.Branches) + { + if (local.IsLocal && local.Upstream == oldUpstream) + { + await new Commands.Branch(_repo.FullPath, local.Name) + .Use(log) + .SetUpstreamAsync(null); + } + } } log.Complete(); diff --git a/src/ViewModels/RenameBranch.cs b/src/ViewModels/RenameBranch.cs index 3e66d420f..1669bfddf 100644 --- a/src/ViewModels/RenameBranch.cs +++ b/src/ViewModels/RenameBranch.cs @@ -28,6 +28,12 @@ public bool AlsoRenameRemote set => SetProperty(ref _alsoRenameRemote, value); } + public bool CanRenameRemote + { + get; + private set; + } + [Required(ErrorMessage = "Branch name is required!!!")] [RegularExpression(@"^[\w\-/\.#\+]+$", ErrorMessage = "Bad branch name format!")] [CustomValidation(typeof(RenameBranch), nameof(ValidateBranchName))] @@ -47,7 +53,10 @@ public RenameBranch(Repository repo, Models.Branch target) { TrackingRemoteBranch = repo.Branches.Find(x => x.FullName == target.Upstream); if (TrackingRemoteBranch != null) + { RenameRemoteTip = App.Text("RenameBranch.WithTrackingRemote", TrackingRemoteBranch.FriendlyName); + CanRenameRemote = target.Head == TrackingRemoteBranch.Head; + } } } @@ -89,13 +98,25 @@ public override async Task Sure() if (_alsoRenameRemote && TrackingRemoteBranch != null) { var remote = TrackingRemoteBranch.Remote; - var pushNew = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{_name}", false); + var pushNew = new Commands.Push(_repo.FullPath, TrackingRemoteBranch.Head, remote, $"refs/heads/{_name}", false, false, false, false); pushNew.Use(log); await pushNew.RunAsync(); - var deleteOld = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{oldName}", true); + var deleteOld = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{TrackingRemoteBranch.Name}", true); deleteOld.Use(log); await deleteOld.RunAsync(); + + // Update local branch's tracking to the new remote branch + var newUpstream = new Models.Branch + { + Name = _name, + FullName = $"refs/remotes/{remote}/{_name}", + Remote = remote, + IsLocal = false, + }; + await new Commands.Branch(_repo.FullPath, _name) + .Use(log) + .SetUpstreamAsync(newUpstream); } } diff --git a/src/ViewModels/RenameRemoteBranch.cs b/src/ViewModels/RenameRemoteBranch.cs index 7d2f36d0e..4d072efc3 100644 --- a/src/ViewModels/RenameRemoteBranch.cs +++ b/src/ViewModels/RenameRemoteBranch.cs @@ -38,6 +38,8 @@ public override async Task Sure() Use(log); var remote = Target.Remote; + var oldUpstream = Target.FullName; + var newUpstream = $"refs/remotes/{remote}/{_name}"; var succ = false; // Push the current commit to the new branch name on the remote @@ -51,6 +53,27 @@ public override async Task Sure() var deleteOld = new Commands.Push(_repo.FullPath, remote, $"refs/heads/{Target.Name}", true); deleteOld.Use(log); succ = await deleteOld.RunAsync(); + + // Update tracking of local branches that tracked the old remote branch + if (succ) + { + foreach (var local in _repo.Branches) + { + if (local.IsLocal && local.Upstream == oldUpstream) + { + var trackingBranch = new Models.Branch + { + Name = _name, + FullName = newUpstream, + Remote = remote, + IsLocal = false, + }; + await new Commands.Branch(_repo.FullPath, local.Name) + .Use(log) + .SetUpstreamAsync(trackingBranch); + } + } + } } log.Complete(); diff --git a/src/Views/RenameBranch.axaml b/src/Views/RenameBranch.axaml index fc7fc1002..a7d6d3772 100644 --- a/src/Views/RenameBranch.axaml +++ b/src/Views/RenameBranch.axaml @@ -41,7 +41,7 @@ - +