diff --git a/README.md b/README.md index acbf22ff..e922c476 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ ## Translation Status -[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-97.55%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-100.00%25-brightgreen)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-94.69%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-93.33%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-94.42%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md) +[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-100.00%25-brightgreen)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-100.00%25-brightgreen)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-94.69%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-93.32%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-94.41%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md) > [!NOTE] > You can find the missing keys in [TRANSLATION.md](TRANSLATION.md) @@ -62,7 +62,7 @@ This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationD | OS | PATH | |---------|-----------------------------------------------------| -| Windows | `C:\Users\USER_NAME\AppData\Roaming\SourceGit` | +| Windows | `%APPDATA%\SourceGit` | | Linux | `${HOME}/.config/SourceGit` or `${HOME}/.sourcegit` | | macOS | `${HOME}/Library/Application Support/SourceGit` | @@ -107,7 +107,7 @@ For **Linux** users: `deb` how to: ```shell curl https://codeberg.org/api/packages/yataro/debian/repository.key | sudo tee /etc/apt/keyrings/sourcegit.asc - echo "deb [signed-by=/etc/apt/keyrings/sourcegit.asc] https://codeberg.org/api/packages/yataro/debian generic main" | sudo tee /etc/apt/sources.list.d/sourcegit.list + echo "deb [signed-by=/etc/apt/keyrings/sourcegit.asc, arch=amd64,arm64] https://codeberg.org/api/packages/yataro/debian generic main" | sudo tee /etc/apt/sources.list.d/sourcegit.list sudo apt update sudo apt install sourcegit ``` diff --git a/TRANSLATION.md b/TRANSLATION.md index 45b0e171..aa42d82b 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -1,27 +1,10 @@ -### de_DE.axaml: 97.55% +### de_DE.axaml: 100.00%
Missing Keys -- Text.Configure.IssueTracker.AddSampleGiteeIssue -- Text.Configure.IssueTracker.AddSampleGiteePullRequest -- Text.Preferences.General.DateFormat -- Text.Preferences.Git.SSLVerify -- Text.Repository.HistoriesLayout -- Text.Repository.HistoriesLayout.Horizontal -- Text.Repository.HistoriesLayout.Vertical -- Text.Repository.HistoriesOrder -- Text.Repository.OnlyHighlightCurrentBranchInHistories -- Text.Repository.Tags.OrderByCreatorDate -- Text.Repository.Tags.OrderByNameAsc -- Text.Repository.Tags.OrderByNameDes -- Text.Repository.Tags.Sort -- Text.Repository.UseRelativeTimeInHistories -- Text.SetUpstream -- Text.SetUpstream.Local -- Text.SetUpstream.Unset -- Text.SetUpstream.Upstream +
@@ -83,7 +66,7 @@ -### it_IT.axaml: 93.33% +### it_IT.axaml: 93.32%
@@ -141,7 +124,7 @@
-### pt_BR.axaml: 94.42% +### pt_BR.axaml: 94.41%
diff --git a/VERSION b/VERSION index 2d67d4ee..d5cfddc3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.02 \ No newline at end of file +2025.03 \ No newline at end of file diff --git a/src/Commands/LFS.cs b/src/Commands/LFS.cs index c9ab7b41..2b7d1de4 100644 --- a/src/Commands/LFS.cs +++ b/src/Commands/LFS.cs @@ -7,7 +7,7 @@ namespace SourceGit.Commands { public partial class LFS { - [GeneratedRegex(@"^(.+)\s+(\w+)\s+\w+:(\d+)$")] + [GeneratedRegex(@"^(.+)\s+([\w.]+)\s+\w+:(\d+)$")] private static partial Regex REG_LOCK(); class SubCmd : Command diff --git a/src/Commands/QueryStashChanges.cs b/src/Commands/QueryStashChanges.cs deleted file mode 100644 index 3b8d2db6..00000000 --- a/src/Commands/QueryStashChanges.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Commands -{ - public partial class QueryStashChanges : Command - { - [GeneratedRegex(@"^(\s?[\w\?]{1,4})\s+(.+)$")] - private static partial Regex REG_FORMAT(); - - public QueryStashChanges(string repo, string sha) - { - WorkingDirectory = repo; - Context = repo; - Args = $"diff --name-status --pretty=format: {sha}^ {sha}"; - } - - public List Result() - { - Exec(); - return _changes; - } - - protected override void OnReadline(string line) - { - var match = REG_FORMAT().Match(line); - if (!match.Success) - return; - - var change = new Models.Change() { Path = match.Groups[2].Value }; - var status = match.Groups[1].Value; - - switch (status[0]) - { - case 'M': - change.Set(Models.ChangeState.Modified); - _changes.Add(change); - break; - case 'A': - change.Set(Models.ChangeState.Added); - _changes.Add(change); - break; - case 'D': - change.Set(Models.ChangeState.Deleted); - _changes.Add(change); - break; - case 'R': - change.Set(Models.ChangeState.Renamed); - _changes.Add(change); - break; - case 'C': - change.Set(Models.ChangeState.Copied); - _changes.Add(change); - break; - } - } - - private readonly List _changes = new List(); - } -} diff --git a/src/Commands/QueryStashes.cs b/src/Commands/QueryStashes.cs index 6d089f8e..ccf601c5 100644 --- a/src/Commands/QueryStashes.cs +++ b/src/Commands/QueryStashes.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace SourceGit.Commands { @@ -8,7 +9,7 @@ namespace SourceGit.Commands { WorkingDirectory = repo; Context = repo; - Args = "stash list --pretty=format:%H%n%ct%n%gd%n%s"; + Args = "stash list --pretty=format:%H%n%P%n%ct%n%gd%n%s"; } public List Result() @@ -26,21 +27,32 @@ namespace SourceGit.Commands _stashes.Add(_current); break; case 1: - _current.Time = ulong.Parse(line); + ParseParent(line); break; case 2: - _current.Name = line; + _current.Time = ulong.Parse(line); break; case 3: + _current.Name = line; + break; + case 4: _current.Message = line; break; } _nextLineIdx++; - if (_nextLineIdx > 3) + if (_nextLineIdx > 4) _nextLineIdx = 0; } + private void ParseParent(string data) + { + if (data.Length < 8) + return; + + _current.Parents.AddRange(data.Split(separator: ' ', options: StringSplitOptions.RemoveEmptyEntries)); + } + private readonly List _stashes = new List(); private Models.Stash _current = null; private int _nextLineIdx = 0; diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs index 40c917dd..1cbf4b2a 100644 --- a/src/Commands/Stash.cs +++ b/src/Commands/Stash.cs @@ -11,9 +11,19 @@ namespace SourceGit.Commands Context = repo; } - public bool Push(string message) + public bool Push(string message, bool includeUntracked = true, bool keepIndex = false) { - Args = $"stash push -m \"{message}\""; + var builder = new StringBuilder(); + builder.Append("stash push "); + if (includeUntracked) + builder.Append("--include-untracked "); + if (keepIndex) + builder.Append("--keep-index "); + builder.Append("-m \""); + builder.Append(message); + builder.Append("\""); + + Args = builder.ToString(); return Exec(); } @@ -65,13 +75,13 @@ namespace SourceGit.Commands public bool Apply(string name) { - Args = $"stash apply -q {name}"; + Args = $"stash apply --index -q {name}"; return Exec(); } public bool Pop(string name) { - Args = $"stash pop -q {name}"; + Args = $"stash pop --index -q {name}"; return Exec(); } diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs index 3d395a84..8dca3bdb 100644 --- a/src/Models/Stash.cs +++ b/src/Models/Stash.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace SourceGit.Models { @@ -6,8 +7,10 @@ namespace SourceGit.Models { public string Name { get; set; } = ""; public string SHA { get; set; } = ""; + public List Parents { get; set; } = []; public ulong Time { get; set; } = 0; public string Message { get; set; } = ""; + public bool HasUntracked => Parents.Count == 3; public string TimeStr => DateTime.UnixEpoch.AddSeconds(Time).ToLocalTime().ToString(DateTimeFormat.Actived.DateTime); } diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs index c63f0e55..7a6c5163 100644 --- a/src/Models/Watcher.cs +++ b/src/Models/Watcher.cs @@ -72,6 +72,11 @@ namespace SourceGit.Models _updateBranch = DateTime.Now.ToFileTime() - 1; } + public void MarkTagDirtyManually() + { + _updateTags = DateTime.Now.ToFileTime() - 1; + } + public void MarkWorkingCopyDirtyManually() { _updateWC = DateTime.Now.ToFileTime() - 1; diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index c0c9d8ff..c7e02431 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -86,6 +86,7 @@ m186 532 287 0 0 287c0 11 9 20 20 20s20-9 20-20l0-287 287 0c11 0 20-9 20-20s-9-20-20-20l-287 0 0-287c0-11-9-20-20-20s-20 9-20 20l0 287-287 0c-11 0-20 9-20 20s9 20 20 20z M432 0h160c27 0 48 21 48 48v336h175c36 0 53 43 28 68L539 757c-15 15-40 15-55 0L180 452c-25-25-7-68 28-68H384V48c0-27 21-48 48-48zm592 752v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h293l98 98c40 40 105 40 145 0l98-98H976c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z M592 768h-160c-27 0-48-21-48-48V384h-175c-36 0-53-43-28-68L485 11c15-15 40-15 55 0l304 304c25 25 7 68-28 68H640v336c0 27-21 48-48 48zm432-16v224c0 27-21 48-48 48H48c-27 0-48-21-48-48V752c0-27 21-48 48-48h272v16c0 62 50 112 112 112h160c62 0 112-50 112-112v-16h272c27 0 48 21 48 48zm-248 176c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40zm128 0c0-22-18-40-40-40s-40 18-40 40s18 40 40 40s40-18 40-40z + M563 555c0 28-23 51-51 51-28 0-51-23-51-51L461 113c0-28 23-51 51-51s51 23 51 51L563 555 563 555zM85 535c0-153 81-287 201-362 24-15 55-8 70 16C371 214 363 245 340 260 248 318 187 419 187 535c0 180 146 325 325 325 180-0 325-146 325-325 0-119-64-223-160-280-24-14-32-46-18-70 14-24 46-32 70-18 125 74 210 211 210 367 0 236-191 427-427 427C276 963 85 772 85 535 M277 85a149 149 0 00-43 292v230a32 32 0 0064 0V555h267A160 160 0 00725 395v-12a149 149 0 10-64-5v17a96 96 0 01-96 96H299V383A149 149 0 00277 85zM228 720a32 32 0 00-37-52 150 150 0 00-53 68 32 32 0 1060 23 85 85 0 0130-39zm136-52a32 32 0 00-37 52 86 86 0 0130 39 32 32 0 1060-23 149 149 0 00-53-68zM204 833a32 32 0 10-55 32 149 149 0 0063 58 32 32 0 0028-57 85 85 0 01-36-33zm202 32a32 32 0 00-55-32 85 85 0 01-36 33 32 32 0 0028 57 149 149 0 0063-58z M467 556c0-0 0-1 0-1C467 555 467 556 467 556zM467 556c0-0 0-0 0-0C467 556 467 556 467 556zM467 556c-0 0-0 0-0 0C467 557 467 557 467 556zM468 549C468 532 468 541 468 549L468 549zM468 549c0 1-0 1-0 2C468 551 468 550 468 549zM468 552c-0 1-0 2-0 3C467 554 468 553 468 552zM736 549C736 532 736 541 736 549L736 549zM289 378l0 179 89 0c-1 80-89 89-89 89l45 45c0 0 129-15 134-134L467 378 289 378zM959 244l0 536c0 99-80 179-179 179L244 959c-99 0-179-80-179-179L65 244c0-99 80-179 179-179l536 0C879 65 959 145 959 244zM869 289c0-74-60-134-134-134L289 155c-74 0-134 60-134 134l0 447c0 74 60 134 134 134l447 0c74 0 134-60 134-134L869 289zM557 557l89 0c-1 80-89 89-89 89l45 45c0 0 129-15 134-134L735 378 557 378 557 557z m224 154a166 166 0 00-166 166v192a166 166 0 00166 166h64v-76h-64a90 90 0 01-90-90v-192a90 90 0 0190-90h320a90 90 0 0190 90v192a90 90 0 01-90 90h-128v77h128a166 166 0 00166-167v-192a166 166 0 00-166-166h-320zm166 390a90 90 0 0190-90h128v-76h-128a166 166 0 00-166 166v192a166 166 0 00166 166h320a166 166 0 00166-166v-192a166 166 0 00-166-166h-64v77h64a90 90 0 0190 90v192a90 90 0 01-90 90h-320a90 90 0 01-90-90v-192z diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index 96dced8d..5143fc37 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -50,7 +50,6 @@ Blame BLAME WIRD BEI DIESER DATEI NICHT UNTERSTÜTZT!!! Auschecken von ${0}$... - Mit Branch vergleichen Mit HEAD vergleichen Mit Worktree vergleichen Branch-Namen kopieren @@ -164,6 +163,8 @@ Aktivere --prune beim fetchen Aktiviere --signoff für Commits TICKETSYSTEM + Beispiel für Gitee Issue Regel einfügen + Beispiel für Gitee Pull Request Regel einfügen Beispiel für Github-Regel hinzufügen Beispiel für Jira-Regel hinzufügen Beispiel für Gitlab Issue Regel einfügen @@ -466,6 +467,7 @@ Tool ALLGEMEIN Beim Starten nach Updates suchen + Datumsformat Sprache Commit-Historie Zeige Autor Zeitpunkt anstatt Commit Zeitpunkt @@ -477,6 +479,7 @@ Benutzer Email Globale Git Benutzer Email Installationspfad + Aktiviere HTTP SSL Verifizierung Benutzername Globaler Git Benutzername Git Version @@ -563,12 +566,17 @@ Aufheben Im Graph ausblenden Im Graph filtern + LAYOUT + Horizontal + Vertikal + COMMIT SORTIERUNG Commit Zeitpunkt (--date-order) Topologie (--topo-order) LOKALE BRANCHES Zum HEAD wechseln Aktiviere '--first-parent' Option Erstelle Branch + Nur aktuellen Branch im Graphen hervorheben Öffne in {0} Öffne in externen Tools Aktualisiern @@ -588,7 +596,12 @@ SUBMODUL AKTUALISIEREN TAGS NEUER TAG + Nach Erstellungsdatum + Nach Namen (Aufsteigend) + Nach Namen (Absteigend) + Sortiere Öffne im Terminal + Verwende relative Zeitangaben in Verlauf WORKTREES WORKTREE HINZUFÜGEN PRUNE @@ -616,6 +629,10 @@ Diese Version überspringen Software Update Es sind momentan kein Updates verfügbar. + Setze verfolgten Branch + Branch: + Upstream Verfolgung aufheben + Upstream: SHA kopieren Zum Commit wechseln Squash Commits diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 1b0059a5..d06f438d 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -47,7 +47,6 @@ Blame BLAME ON THIS FILE IS NOT SUPPORTED!!! Checkout ${0}$... - Compare with Branch Compare with HEAD Compare with Worktree Copy Branch Name diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 7f6bad97..74b2f224 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -50,7 +50,6 @@ Blame ¡BLAME EN ESTE ARCHIVO NO SOPORTADO! Checkout ${0}$... - Comparar con Rama Comparar con HEAD Comparar con Worktree Copiar Nombre de Rama diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 1bfd5284..e4bb9c26 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -50,7 +50,6 @@ Blâme LE BLÂME SUR CE FICHIER N'EST PAS SUPPORTÉ!!! Récupérer ${0}$... - Comparer avec la branche Comparer avec HEAD Comparer avec le worktree Copier le nom de la branche diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index e63a4737..72e2aa28 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -50,7 +50,6 @@ Attribuisci L'ATTRIBUZIONE SU QUESTO FILE NON È SUPPORTATA!!! Checkout ${0}$... - Confronta con Branch Confronta con HEAD Confronta con Worktree Copia Nome Branch diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 5d29caca..8b17bdaf 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -75,7 +75,6 @@ Blame BLAME NESTE ARQUIVO NÃO É SUPORTADO!!! Checkout ${0}$... - Comparar com Branch Comparar com HEAD Comparar com Worktree Copiar Nome do Branch diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 725f378c..75650a28 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -50,7 +50,6 @@ Расследование РАССЛЕДОВАНИЕ В ЭТОМ ФАЙЛЕ НЕ ПОДДЕРЖИВАЕТСЯ!!! Проверить ${0}$... - Сравнить с веткой Сравнить с ГОЛОВОЙ (HEAD) Сравнить с рабочим каталогом Копировать имя ветки diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 81d0d80f..b5541376 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -50,7 +50,6 @@ 逐行追溯(blame) 选中文件不支持该操作!!! 检出(checkout) ${0}$... - 与其他分支对比 与当前HEAD比较 与本地工作树比较 复制分支名 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 4fb4f375..d2395e58 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -50,7 +50,6 @@ 逐行溯源 (blame) 所選擇的檔案不支援該操作! 簽出 (checkout) ${0}$... - 與其他分支比較 與目前 HEAD 比較 與本機工作區比較 複製分支名稱 diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index 64168b2e..7c098207 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -49,7 +49,7 @@ - + diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index 01f39b35..d90e97d6 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -420,34 +420,37 @@ namespace SourceGit.ViewModels menu.Items.Add(patch); menu.Items.Add(new MenuItem { Header = "-" }); - var resetToThisRevision = new MenuItem(); - resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); - resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); - resetToThisRevision.Click += (_, ev) => + if (!_repo.IsBare) { - new Commands.Checkout(_repo.FullPath).FileWithRevision(change.Path, $"{_commit.SHA}"); - ev.Handled = true; - }; + var resetToThisRevision = new MenuItem(); + resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); + resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Click += (_, ev) => + { + new Commands.Checkout(_repo.FullPath).FileWithRevision(change.Path, $"{_commit.SHA}"); + ev.Handled = true; + }; - var resetToFirstParent = new MenuItem(); - resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision"); - resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout"); - resetToFirstParent.IsEnabled = _commit.Parents.Count > 0; - resetToFirstParent.Click += (_, ev) => - { - if (change.Index == Models.ChangeState.Renamed) - new Commands.Checkout(_repo.FullPath).FileWithRevision(change.OriginalPath, $"{_commit.SHA}~1"); + var resetToFirstParent = new MenuItem(); + resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision"); + resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToFirstParent.IsEnabled = _commit.Parents.Count > 0; + resetToFirstParent.Click += (_, ev) => + { + if (change.Index == Models.ChangeState.Renamed) + new Commands.Checkout(_repo.FullPath).FileWithRevision(change.OriginalPath, $"{_commit.SHA}~1"); - new Commands.Checkout(_repo.FullPath).FileWithRevision(change.Path, $"{_commit.SHA}~1"); - ev.Handled = true; - }; + new Commands.Checkout(_repo.FullPath).FileWithRevision(change.Path, $"{_commit.SHA}~1"); + ev.Handled = true; + }; - menu.Items.Add(resetToThisRevision); - menu.Items.Add(resetToFirstParent); - menu.Items.Add(new MenuItem { Header = "-" }); + menu.Items.Add(resetToThisRevision); + menu.Items.Add(resetToFirstParent); + menu.Items.Add(new MenuItem { Header = "-" }); - if (File.Exists(Path.Combine(fullPath))) - TryToAddContextMenuItemsForGitLFS(menu, change.Path); + if (File.Exists(Path.Combine(fullPath))) + TryToAddContextMenuItemsForGitLFS(menu, change.Path); + } var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); diff --git a/src/ViewModels/CreateBranch.cs b/src/ViewModels/CreateBranch.cs index 64806a49..b67a453a 100644 --- a/src/ViewModels/CreateBranch.cs +++ b/src/ViewModels/CreateBranch.cs @@ -31,6 +31,11 @@ namespace SourceGit.ViewModels set => _repo.Settings.CheckoutBranchOnCreateBranch = value; } + public bool IsBareRepository + { + get => _repo.IsBare; + } + public CreateBranch(Repository repo, Models.Branch branch) { _repo = repo; @@ -84,7 +89,7 @@ namespace SourceGit.ViewModels return Task.Run(() => { var succ = false; - if (CheckoutAfterCreated) + if (CheckoutAfterCreated && !_repo.IsBare) { var changes = new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).Result(); var needPopStash = false; diff --git a/src/ViewModels/DeleteTag.cs b/src/ViewModels/DeleteTag.cs index 7d631a38..7b53e798 100644 --- a/src/ViewModels/DeleteTag.cs +++ b/src/ViewModels/DeleteTag.cs @@ -33,7 +33,11 @@ namespace SourceGit.ViewModels { var remotes = ShouldPushToRemote ? _repo.Remotes : null; var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, remotes); - CallUIThread(() => _repo.SetWatcherEnabled(true)); + CallUIThread(() => + { + _repo.MarkTagsDirtyManually(); + _repo.SetWatcherEnabled(true); + }); return succ; }); } diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index b890b76e..b5754d33 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -213,7 +213,7 @@ namespace SourceGit.ViewModels { if (firstRemoteBranch != null) _repo.ShowPopup(new CreateBranch(_repo, firstRemoteBranch)); - else + else if (!_repo.IsBare) _repo.ShowPopup(new CheckoutCommit(_repo, commit)); } } @@ -253,36 +253,39 @@ namespace SourceGit.ViewModels var multipleMenu = new ContextMenu(); - if (canCherryPick) + if (!_repo.IsBare) { - var cherryPickMultiple = new MenuItem(); - cherryPickMultiple.Header = App.Text("CommitCM.CherryPickMultiple"); - cherryPickMultiple.Icon = App.CreateMenuIcon("Icons.CherryPick"); - cherryPickMultiple.Click += (_, e) => + if (canCherryPick) { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new CherryPick(_repo, selected)); - e.Handled = true; - }; - multipleMenu.Items.Add(cherryPickMultiple); - } + var cherryPickMultiple = new MenuItem(); + cherryPickMultiple.Header = App.Text("CommitCM.CherryPickMultiple"); + cherryPickMultiple.Icon = App.CreateMenuIcon("Icons.CherryPick"); + cherryPickMultiple.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new CherryPick(_repo, selected)); + e.Handled = true; + }; + multipleMenu.Items.Add(cherryPickMultiple); + } - if (canMerge) - { - var mergeMultiple = new MenuItem(); - mergeMultiple.Header = App.Text("CommitCM.MergeMultiple"); - mergeMultiple.Icon = App.CreateMenuIcon("Icons.Merge"); - mergeMultiple.Click += (_, e) => + if (canMerge) { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new MergeMultiple(_repo, selected)); - e.Handled = true; - }; - multipleMenu.Items.Add(mergeMultiple); - } + var mergeMultiple = new MenuItem(); + mergeMultiple.Header = App.Text("CommitCM.MergeMultiple"); + mergeMultiple.Icon = App.CreateMenuIcon("Icons.Merge"); + mergeMultiple.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new MergeMultiple(_repo, selected)); + e.Handled = true; + }; + multipleMenu.Items.Add(mergeMultiple); + } - if (canCherryPick || canMerge) - multipleMenu.Items.Add(new MenuItem() { Header = "-" }); + if (canCherryPick || canMerge) + multipleMenu.Items.Add(new MenuItem() { Header = "-" }); + } var saveToPatchMultiple = new MenuItem(); saveToPatchMultiple.Icon = App.CreateMenuIcon("Icons.Diff"); @@ -394,25 +397,48 @@ namespace SourceGit.ViewModels menu.Items.Add(new MenuItem() { Header = "-" }); } - if (current.Head != commit.SHA) + if (!_repo.IsBare) { - var reset = new MenuItem(); - reset.Header = new Views.NameHighlightedTextBlock("CommitCM.Reset", current.Name); - reset.Icon = App.CreateMenuIcon("Icons.Reset"); - reset.Click += (_, e) => + if (current.Head != commit.SHA) { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Reset(_repo, current, commit)); - e.Handled = true; - }; - menu.Items.Add(reset); + var reset = new MenuItem(); + reset.Header = new Views.NameHighlightedTextBlock("CommitCM.Reset", current.Name); + reset.Icon = App.CreateMenuIcon("Icons.Reset"); + reset.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new Reset(_repo, current, commit)); + e.Handled = true; + }; + menu.Items.Add(reset); - if (commit.IsMerged) + if (commit.IsMerged) + { + var squash = new MenuItem(); + squash.Header = App.Text("CommitCM.SquashCommitsSinceThis"); + squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); + squash.Click += (_, e) => + { + if (_repo.LocalChangesCount > 0) + { + App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); + return; + } + + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new Squash(_repo, commit, commit.SHA)); + + e.Handled = true; + }; + menu.Items.Add(squash); + } + } + else { - var squash = new MenuItem(); - squash.Header = App.Text("CommitCM.SquashCommitsSinceThis"); - squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); - squash.Click += (_, e) => + var reword = new MenuItem(); + reword.Header = App.Text("CommitCM.Reword"); + reword.Icon = App.CreateMenuIcon("Icons.Edit"); + reword.Click += (_, e) => { if (_repo.LocalChangesCount > 0) { @@ -421,169 +447,149 @@ namespace SourceGit.ViewModels } if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Squash(_repo, commit, commit.SHA)); + _repo.ShowPopup(new Reword(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(reword); + + var squash = new MenuItem(); + squash.Header = App.Text("CommitCM.Squash"); + squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); + squash.IsEnabled = commit.Parents.Count == 1; + squash.Click += (_, e) => + { + if (_repo.LocalChangesCount > 0) + { + App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); + return; + } + + if (commit.Parents.Count == 1) + { + var parent = _commits.Find(x => x.SHA == commit.Parents[0]); + if (parent != null && _repo.CanCreatePopup()) + _repo.ShowPopup(new Squash(_repo, parent, commit.SHA)); + } e.Handled = true; }; menu.Items.Add(squash); } - } - else - { - var reword = new MenuItem(); - reword.Header = App.Text("CommitCM.Reword"); - reword.Icon = App.CreateMenuIcon("Icons.Edit"); - reword.Click += (_, e) => + + if (!commit.IsMerged) { - if (_repo.LocalChangesCount > 0) - { - App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); - return; - } - - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Reword(_repo, commit)); - e.Handled = true; - }; - menu.Items.Add(reword); - - var squash = new MenuItem(); - squash.Header = App.Text("CommitCM.Squash"); - squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); - squash.IsEnabled = commit.Parents.Count == 1; - squash.Click += (_, e) => - { - if (_repo.LocalChangesCount > 0) - { - App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); - return; - } - - if (commit.Parents.Count == 1) - { - var parent = _commits.Find(x => x.SHA == commit.Parents[0]); - if (parent != null && _repo.CanCreatePopup()) - _repo.ShowPopup(new Squash(_repo, parent, commit.SHA)); - } - - e.Handled = true; - }; - menu.Items.Add(squash); - } - - if (!commit.IsMerged) - { - var rebase = new MenuItem(); - rebase.Header = new Views.NameHighlightedTextBlock("CommitCM.Rebase", current.Name); - rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); - rebase.Click += (_, e) => - { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Rebase(_repo, current, commit)); - e.Handled = true; - }; - menu.Items.Add(rebase); - - if (!commit.HasDecorators) - { - var merge = new MenuItem(); - merge.Header = new Views.NameHighlightedTextBlock("CommitCM.Merge", current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); - merge.Click += (_, e) => + var rebase = new MenuItem(); + rebase.Header = new Views.NameHighlightedTextBlock("CommitCM.Rebase", current.Name); + rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); + rebase.Click += (_, e) => { if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Merge(_repo, commit, current.Name)); + _repo.ShowPopup(new Rebase(_repo, current, commit)); + e.Handled = true; + }; + menu.Items.Add(rebase); + + if (!commit.HasDecorators) + { + var merge = new MenuItem(); + merge.Header = new Views.NameHighlightedTextBlock("CommitCM.Merge", current.Name); + merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new Merge(_repo, commit, current.Name)); + + e.Handled = true; + }; + menu.Items.Add(merge); + } + + var cherryPick = new MenuItem(); + cherryPick.Header = App.Text("CommitCM.CherryPick"); + cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick"); + cherryPick.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + { + if (commit.Parents.Count <= 1) + { + _repo.ShowPopup(new CherryPick(_repo, [commit])); + } + else + { + var parents = new List(); + foreach (var sha in commit.Parents) + { + var parent = _commits.Find(x => x.SHA == sha); + if (parent == null) + parent = new Commands.QuerySingleCommit(_repo.FullPath, sha).Result(); + + if (parent != null) + parents.Add(parent); + } + + _repo.ShowPopup(new CherryPick(_repo, commit, parents)); + } + } e.Handled = true; }; - menu.Items.Add(merge); + menu.Items.Add(cherryPick); + } + else + { + var revert = new MenuItem(); + revert.Header = App.Text("CommitCM.Revert"); + revert.Icon = App.CreateMenuIcon("Icons.Undo"); + revert.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new Revert(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(revert); } - var cherryPick = new MenuItem(); - cherryPick.Header = App.Text("CommitCM.CherryPick"); - cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick"); - cherryPick.Click += (_, e) => + if (current.Head != commit.SHA) { - if (_repo.CanCreatePopup()) + var checkoutCommit = new MenuItem(); + checkoutCommit.Header = App.Text("CommitCM.Checkout"); + checkoutCommit.Icon = App.CreateMenuIcon("Icons.Detached"); + checkoutCommit.Click += (_, e) => { - if (commit.Parents.Count <= 1) - { - _repo.ShowPopup(new CherryPick(_repo, [commit])); - } - else - { - var parents = new List(); - foreach (var sha in commit.Parents) - { - var parent = _commits.Find(x => x.SHA == sha); - if (parent == null) - parent = new Commands.QuerySingleCommit(_repo.FullPath, sha).Result(); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new CheckoutCommit(_repo, commit)); + e.Handled = true; + }; + menu.Items.Add(checkoutCommit); + } - if (parent != null) - parents.Add(parent); - } - - _repo.ShowPopup(new CherryPick(_repo, commit, parents)); - } - } - - e.Handled = true; - }; - menu.Items.Add(cherryPick); - } - else - { - var revert = new MenuItem(); - revert.Header = App.Text("CommitCM.Revert"); - revert.Icon = App.CreateMenuIcon("Icons.Undo"); - revert.Click += (_, e) => - { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Revert(_repo, commit)); - e.Handled = true; - }; - menu.Items.Add(revert); - } - - if (current.Head != commit.SHA) - { - var checkoutCommit = new MenuItem(); - checkoutCommit.Header = App.Text("CommitCM.Checkout"); - checkoutCommit.Icon = App.CreateMenuIcon("Icons.Detached"); - checkoutCommit.Click += (_, e) => - { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new CheckoutCommit(_repo, commit)); - e.Handled = true; - }; - menu.Items.Add(checkoutCommit); - } - - menu.Items.Add(new MenuItem() { Header = "-" }); - - if (commit.IsMerged && current.Head != commit.SHA) - { - var interactiveRebase = new MenuItem(); - interactiveRebase.Header = new Views.NameHighlightedTextBlock("CommitCM.InteractiveRebase", current.Name); - interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); - interactiveRebase.Click += (_, e) => - { - if (_repo.LocalChangesCount > 0) - { - App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); - return; - } - - App.OpenDialog(new Views.InteractiveRebase() - { - DataContext = new InteractiveRebase(_repo, current, commit) - }); - - e.Handled = true; - }; - menu.Items.Add(interactiveRebase); menu.Items.Add(new MenuItem() { Header = "-" }); - } + + if (commit.IsMerged && current.Head != commit.SHA) + { + var interactiveRebase = new MenuItem(); + interactiveRebase.Header = new Views.NameHighlightedTextBlock("CommitCM.InteractiveRebase", current.Name); + interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); + interactiveRebase.Click += (_, e) => + { + if (_repo.LocalChangesCount > 0) + { + App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first."); + return; + } + + App.OpenDialog(new Views.InteractiveRebase() + { + DataContext = new InteractiveRebase(_repo, current, commit) + }); + + e.Handled = true; + }; + menu.Items.Add(interactiveRebase); + menu.Items.Add(new MenuItem() { Header = "-" }); + } + } if (current.Head != commit.SHA) { @@ -914,21 +920,24 @@ namespace SourceGit.ViewModels submenu.Items.Add(rename); submenu.Items.Add(new MenuItem() { Header = "-" }); - var detect = Commands.GitFlow.DetectType(_repo.FullPath, _repo.Branches, current.Name); - if (detect.IsGitFlowBranch) + if (!_repo.IsBare) { - var finish = new MenuItem(); - finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", current.Name); - finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); - finish.Click += (_, e) => + var detect = Commands.GitFlow.DetectType(_repo.FullPath, _repo.Branches, current.Name); + if (detect.IsGitFlowBranch) { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new GitFlowFinish(_repo, current, detect.Type, detect.Prefix)); - e.Handled = true; - }; - submenu.Items.Add(finish); - submenu.Items.Add(new MenuItem() { Header = "-" }); - } + var finish = new MenuItem(); + finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", current.Name); + finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); + finish.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new GitFlowFinish(_repo, current, detect.Type, detect.Prefix)); + e.Handled = true; + }; + submenu.Items.Add(finish); + submenu.Items.Add(new MenuItem() { Header = "-" }); + } + } var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); @@ -951,27 +960,30 @@ namespace SourceGit.ViewModels FillBranchVisibilityMenu(submenu, branch); - var checkout = new MenuItem(); - checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); - checkout.Icon = App.CreateMenuIcon("Icons.Check"); - checkout.Click += (_, e) => + if (!_repo.IsBare) { - _repo.CheckoutBranch(branch); - e.Handled = true; - }; - submenu.Items.Add(checkout); + var checkout = new MenuItem(); + checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); + checkout.Icon = App.CreateMenuIcon("Icons.Check"); + checkout.Click += (_, e) => + { + _repo.CheckoutBranch(branch); + e.Handled = true; + }; + submenu.Items.Add(checkout); - var merge = new MenuItem(); - merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); - merge.IsEnabled = !merged; - merge.Click += (_, e) => - { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Merge(_repo, branch, current.Name)); - e.Handled = true; - }; - submenu.Items.Add(merge); + var merge = new MenuItem(); + merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name); + merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.IsEnabled = !merged; + merge.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new Merge(_repo, branch, current.Name)); + e.Handled = true; + }; + submenu.Items.Add(merge); + } var rename = new MenuItem(); rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name); @@ -996,21 +1008,24 @@ namespace SourceGit.ViewModels submenu.Items.Add(delete); submenu.Items.Add(new MenuItem() { Header = "-" }); - var detect = Commands.GitFlow.DetectType(_repo.FullPath, _repo.Branches, branch.Name); - if (detect.IsGitFlowBranch) + if (!_repo.IsBare) { - var finish = new MenuItem(); - finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name); - finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); - finish.Click += (_, e) => + var detect = Commands.GitFlow.DetectType(_repo.FullPath, _repo.Branches, branch.Name); + if (detect.IsGitFlowBranch) { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new GitFlowFinish(_repo, branch, detect.Type, detect.Prefix)); - e.Handled = true; - }; - submenu.Items.Add(finish); - submenu.Items.Add(new MenuItem() { Header = "-" }); - } + var finish = new MenuItem(); + finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name); + finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); + finish.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new GitFlowFinish(_repo, branch, detect.Type, detect.Prefix)); + e.Handled = true; + }; + submenu.Items.Add(finish); + submenu.Items.Add(new MenuItem() { Header = "-" }); + } + } var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); @@ -1104,17 +1119,19 @@ namespace SourceGit.ViewModels }; submenu.Items.Add(push); - var merge = new MenuItem(); - merge.Header = new Views.NameHighlightedTextBlock("TagCM.Merge", tag.Name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); - merge.IsEnabled = !merged; - merge.Click += (_, e) => + if (!_repo.IsBare && !merged) { - if (_repo.CanCreatePopup()) - _repo.ShowPopup(new Merge(_repo, tag, current.Name)); - e.Handled = true; - }; - submenu.Items.Add(merge); + var merge = new MenuItem(); + merge.Header = new Views.NameHighlightedTextBlock("TagCM.Merge", tag.Name, current.Name); + merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Click += (_, e) => + { + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new Merge(_repo, tag, current.Name)); + e.Handled = true; + }; + submenu.Items.Add(merge); + } var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name); diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 57a1c404..6761eccf 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -280,19 +280,20 @@ namespace SourceGit.ViewModels return; } - var gitDir = new Commands.QueryGitDir(node.Id).Result(); - if (string.IsNullOrEmpty(gitDir)) + var isBare = new Commands.IsBareRepository(node.Id).Result(); + var gitDir = node.Id; + if (!isBare) { - var ctx = page == null ? ActivePage.Node.Id : page.Node.Id; - App.RaiseException(ctx, "Given path is not a valid git repository!"); - return; + gitDir = new Commands.QueryGitDir(node.Id).Result(); + if (string.IsNullOrEmpty(gitDir)) + { + var ctx = page == null ? ActivePage.Node.Id : page.Node.Id; + App.RaiseException(ctx, "Given path is not a valid git repository!"); + return; + } } - var repo = new Repository() - { - FullPath = node.Id, - GitDir = gitDir, - }; + var repo = new Repository(isBare, node.Id, gitDir); repo.Open(); if (page == null) diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index c317c2b8..7f619f1e 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -18,6 +18,11 @@ namespace SourceGit.ViewModels { public class Repository : ObservableObject, Models.IRepository { + public bool IsBare + { + get; + } + public string FullPath { get => _fullpath; @@ -448,6 +453,13 @@ namespace SourceGit.ViewModels private set => SetProperty(ref _isAutoFetching, value); } + public Repository(bool isBare, string path, string gitDir) + { + IsBare = isBare; + FullPath = path; + GitDir = gitDir; + } + public void Open() { var settingsFile = Path.Combine(_gitDir, "sourcegit.settings"); @@ -759,6 +771,19 @@ namespace SourceGit.ViewModels } } + public void MarkTagsDirtyManually() + { + if (_watcher == null) + { + Task.Run(RefreshTags); + Task.Run(RefreshCommits); + } + else + { + _watcher.MarkTagDirtyManually(); + } + } + public void MarkWorkingCopyDirtyManually() { if (_watcher == null) @@ -995,6 +1020,9 @@ namespace SourceGit.ViewModels public void RefreshWorkingCopyChanges() { + if (IsBare) + return; + var changes = new Commands.QueryLocalChanges(_fullpath, _settings.IncludeUntrackedInLocalChanges).Result(); if (_workingCopy == null) return; @@ -1010,6 +1038,9 @@ namespace SourceGit.ViewModels public void RefreshStashes() { + if (IsBare) + return; + var stashes = new Commands.QueryStashes(_fullpath).Result(); Dispatcher.UIThread.Invoke(() => { @@ -1044,6 +1075,9 @@ namespace SourceGit.ViewModels } } + if (IsBare) + return; + if (!CanCreatePopup()) return; @@ -1416,73 +1450,72 @@ namespace SourceGit.ViewModels if (branch.IsCurrent) { - var discard = new MenuItem(); - discard.Header = App.Text("BranchCM.DiscardAll"); - discard.Icon = App.CreateMenuIcon("Icons.Undo"); - discard.Click += (_, e) => + if (!IsBare) { - if (CanCreatePopup()) - ShowPopup(new Discard(this)); - e.Handled = true; - }; - - menu.Items.Add(discard); - menu.Items.Add(new MenuItem() { Header = "-" }); - - if (!string.IsNullOrEmpty(branch.Upstream)) - { - var upstream = branch.Upstream.Substring(13); - var fastForward = new MenuItem(); - fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream); - fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); - fastForward.IsEnabled = branch.TrackStatus.Ahead.Count == 0; - fastForward.Click += (_, e) => + var discard = new MenuItem(); + discard.Header = App.Text("BranchCM.DiscardAll"); + discard.Icon = App.CreateMenuIcon("Icons.Undo"); + discard.Click += (_, e) => { - var b = _branches.Find(x => x.FriendlyName == upstream); - if (b == null) - return; - if (CanCreatePopup()) - ShowAndStartPopup(new Merge(this, b, branch.Name)); - + ShowPopup(new Discard(this)); e.Handled = true; }; - var pull = new MenuItem(); - pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream); - pull.Icon = App.CreateMenuIcon("Icons.Pull"); - pull.Click += (_, e) => - { - if (CanCreatePopup()) - ShowPopup(new Pull(this, null)); - e.Handled = true; - }; + menu.Items.Add(discard); + menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(fastForward); - menu.Items.Add(pull); - } + if (!string.IsNullOrEmpty(branch.Upstream)) + { + var upstream = branch.Upstream.Substring(13); + var fastForward = new MenuItem(); + fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream); + fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); + fastForward.IsEnabled = branch.TrackStatus.Ahead.Count == 0; + fastForward.Click += (_, e) => + { + var b = _branches.Find(x => x.FriendlyName == upstream); + if (b == null) + return; + + if (CanCreatePopup()) + ShowAndStartPopup(new Merge(this, b, branch.Name)); + + e.Handled = true; + }; + + var pull = new MenuItem(); + pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream); + pull.Icon = App.CreateMenuIcon("Icons.Pull"); + pull.Click += (_, e) => + { + if (CanCreatePopup()) + ShowPopup(new Pull(this, null)); + e.Handled = true; + }; + + menu.Items.Add(fastForward); + menu.Items.Add(pull); + } + } menu.Items.Add(push); - - var compareWithBranch = CreateMenuItemToCompareBranches(branch); - if (compareWithBranch != null) - { - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(compareWithBranch); - } } else { - var checkout = new MenuItem(); - checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); - checkout.Icon = App.CreateMenuIcon("Icons.Check"); - checkout.Click += (_, e) => + if (!IsBare) { - CheckoutBranch(branch); - e.Handled = true; - }; - menu.Items.Add(checkout); - menu.Items.Add(new MenuItem() { Header = "-" }); + var checkout = new MenuItem(); + checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); + checkout.Icon = App.CreateMenuIcon("Icons.Check"); + checkout.Click += (_, e) => + { + CheckoutBranch(branch); + e.Handled = true; + }; + menu.Items.Add(checkout); + menu.Items.Add(new MenuItem() { Header = "-" }); + } var worktree = _worktrees.Find(x => x.Branch == branch.FullName); var upstream = _branches.Find(x => x.FullName == branch.Upstream); @@ -1517,28 +1550,44 @@ namespace SourceGit.ViewModels menu.Items.Add(push); - var merge = new MenuItem(); - merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, _currentBranch.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); - merge.Click += (_, e) => + if (!IsBare) { - if (CanCreatePopup()) - ShowPopup(new Merge(this, branch, _currentBranch.Name)); - e.Handled = true; - }; + var merge = new MenuItem(); + merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, _currentBranch.Name); + merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Click += (_, e) => + { + if (CanCreatePopup()) + ShowPopup(new Merge(this, branch, _currentBranch.Name)); + e.Handled = true; + }; - var rebase = new MenuItem(); - rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", _currentBranch.Name, branch.Name); - rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); - rebase.Click += (_, e) => + var rebase = new MenuItem(); + rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", _currentBranch.Name, branch.Name); + rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); + rebase.Click += (_, e) => + { + if (CanCreatePopup()) + ShowPopup(new Rebase(this, _currentBranch, branch)); + e.Handled = true; + }; + + menu.Items.Add(merge); + menu.Items.Add(rebase); + } + + var compareWithHead = new MenuItem(); + compareWithHead.Header = App.Text("BranchCM.CompareWithHead"); + compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithHead.Click += (_, _) => { - if (CanCreatePopup()) - ShowPopup(new Rebase(this, _currentBranch, branch)); - e.Handled = true; + App.OpenDialog(new Views.BranchCompare() + { + DataContext = new BranchCompare(_fullpath, branch, _currentBranch) + }); }; - - menu.Items.Add(merge); - menu.Items.Add(rebase); + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(compareWithHead); if (_localChangesCount > 0) { @@ -1556,35 +1605,28 @@ namespace SourceGit.ViewModels _histories.DetailContext = new RevisionCompare(_fullpath, target, null); } }; - menu.Items.Add(new MenuItem() { Header = "-" }); menu.Items.Add(compareWithWorktree); } - - var compareWithBranch = CreateMenuItemToCompareBranches(branch); - if (compareWithBranch != null) - { - if (_localChangesCount == 0) - menu.Items.Add(new MenuItem() { Header = "-" }); - - menu.Items.Add(compareWithBranch); - } } - var detect = Commands.GitFlow.DetectType(_fullpath, _branches, branch.Name); - if (detect.IsGitFlowBranch) + if (!IsBare) { - var finish = new MenuItem(); - finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name); - finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); - finish.Click += (_, e) => + var detect = Commands.GitFlow.DetectType(_fullpath, _branches, branch.Name); + if (detect.IsGitFlowBranch) { - if (CanCreatePopup()) - ShowPopup(new GitFlowFinish(this, branch, detect.Type, detect.Prefix)); - e.Handled = true; - }; - menu.Items.Add(new MenuItem() { Header = "-" }); - menu.Items.Add(finish); - } + var finish = new MenuItem(); + finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name); + finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); + finish.Click += (_, e) => + { + if (CanCreatePopup()) + ShowPopup(new GitFlowFinish(this, branch, detect.Type, detect.Prefix)); + e.Handled = true; + }; + menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(finish); + } + } var rename = new MenuItem(); rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name); @@ -1635,26 +1677,29 @@ namespace SourceGit.ViewModels menu.Items.Add(createTag); menu.Items.Add(new MenuItem() { Header = "-" }); - var remoteBranches = new List(); - foreach (var b in _branches) + if (!IsBare) { - if (!b.IsLocal) - remoteBranches.Add(b); - } - - if (remoteBranches.Count > 0) - { - var tracking = new MenuItem(); - tracking.Header = App.Text("BranchCM.Tracking"); - tracking.Icon = App.CreateMenuIcon("Icons.Track"); - tracking.Click += (_, e) => + var remoteBranches = new List(); + foreach (var b in _branches) { - if (CanCreatePopup()) - ShowPopup(new SetUpstream(this, branch, remoteBranches)); - e.Handled = true; - }; - menu.Items.Add(tracking); - } + if (!b.IsLocal) + remoteBranches.Add(b); + } + + if (remoteBranches.Count > 0) + { + var tracking = new MenuItem(); + tracking.Header = App.Text("BranchCM.Tracking"); + tracking.Icon = App.CreateMenuIcon("Icons.Track"); + tracking.Click += (_, e) => + { + if (CanCreatePopup()) + ShowPopup(new SetUpstream(this, branch, remoteBranches)); + e.Handled = true; + }; + menu.Items.Add(tracking); + } + } var archive = new MenuItem(); archive.Icon = App.CreateMenuIcon("Icons.Archive"); @@ -1813,7 +1858,18 @@ namespace SourceGit.ViewModels menu.Items.Add(new MenuItem() { Header = "-" }); } - var hasCompare = false; + var compareWithHead = new MenuItem(); + compareWithHead.Header = App.Text("BranchCM.CompareWithHead"); + compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithHead.Click += (_, _) => + { + App.OpenDialog(new Views.BranchCompare() + { + DataContext = new BranchCompare(_fullpath, branch, _currentBranch) + }); + }; + menu.Items.Add(compareWithHead); + if (_localChangesCount > 0) { var compareWithWorktree = new MenuItem(); @@ -1831,18 +1887,8 @@ namespace SourceGit.ViewModels } }; menu.Items.Add(compareWithWorktree); - hasCompare = true; } - - var compareWithBranch = CreateMenuItemToCompareBranches(branch); - if (compareWithBranch != null) - { - menu.Items.Add(compareWithBranch); - hasCompare = true; - } - - if (hasCompare) - menu.Items.Add(new MenuItem() { Header = "-" }); + menu.Items.Add(new MenuItem() { Header = "-" }); var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name); @@ -2078,39 +2124,6 @@ namespace SourceGit.ViewModels return menu; } - private MenuItem CreateMenuItemToCompareBranches(Models.Branch branch) - { - if (_branches.Count == 1) - return null; - - var compare = new MenuItem(); - compare.Header = App.Text("BranchCM.CompareWithBranch"); - compare.Icon = App.CreateMenuIcon("Icons.Compare"); - - foreach (var b in _branches) - { - if (b.FullName != branch.FullName) - { - var dup = b; - var target = new MenuItem(); - target.Header = b.FriendlyName; - target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch"); - target.Click += (_, e) => - { - App.OpenDialog(new Views.BranchCompare() - { - DataContext = new BranchCompare(_fullpath, branch, dup) - }); - e.Handled = true; - }; - - compare.Items.Add(target); - } - } - - return compare; - } - private LauncherPage GetOwnerPage() { var launcher = App.GetLauncer(); diff --git a/src/ViewModels/ScanRepositories.cs b/src/ViewModels/ScanRepositories.cs index 5c023587..8d14e36e 100644 --- a/src/ViewModels/ScanRepositories.cs +++ b/src/ViewModels/ScanRepositories.cs @@ -34,7 +34,7 @@ namespace SourceGit.ViewModels watch.Start(); var rootDir = new DirectoryInfo(RootDir); - var founded = new List(); + var founded = new List(); GetUnmanagedRepositories(rootDir, founded, new EnumerationOptions() { AttributesToSkip = FileAttributes.Hidden | FileAttributes.System, @@ -47,16 +47,16 @@ namespace SourceGit.ViewModels foreach (var f in founded) { - var parent = new DirectoryInfo(f).Parent!.FullName.Replace("\\", "/"); + var parent = new DirectoryInfo(f.Path).Parent!.FullName.Replace("\\", "/"); if (parent.Equals(normalizedRoot, StringComparison.Ordinal)) { - Preferences.Instance.FindOrAddNodeByRepositoryPath(f, null, false); + Preferences.Instance.FindOrAddNodeByRepositoryPath(f.Path, null, false); } else if (parent.StartsWith(normalizedRoot, StringComparison.Ordinal)) { var relative = parent.Substring(normalizedRoot.Length).TrimStart('/'); var group = FindOrCreateGroupRecursive(Preferences.Instance.RepositoryNodes, relative); - Preferences.Instance.FindOrAddNodeByRepositoryPath(f, group, false); + Preferences.Instance.FindOrAddNodeByRepositoryPath(f.Path, group, false); } } @@ -85,7 +85,7 @@ namespace SourceGit.ViewModels } } - private void GetUnmanagedRepositories(DirectoryInfo dir, List outs, EnumerationOptions opts, int depth = 0) + private void GetUnmanagedRepositories(DirectoryInfo dir, List outs, EnumerationOptions opts, int depth = 0) { var subdirs = dir.GetDirectories("*", opts); foreach (var subdir in subdirs) @@ -111,12 +111,19 @@ namespace SourceGit.ViewModels { var normalized = test.StdOut.Trim().Replace("\\", "/"); if (!_managed.Contains(normalized)) - outs.Add(normalized); + outs.Add(new FoundRepository(normalized, false)); } continue; } + var isBare = new Commands.IsBareRepository(subdir.FullName).Result(); + if (isBare) + { + outs.Add(new FoundRepository(normalizedSelf, true)); + continue; + } + if (depth < 5) GetUnmanagedRepositories(subdir, outs, opts, depth + 1); } @@ -161,6 +168,12 @@ namespace SourceGit.ViewModels return added; } + private record FoundRepository(string path, bool isBare) + { + public string Path { get; set; } = path; + public bool IsBare { get; set; } = isBare; + } + private HashSet _managed = new HashSet(); } } diff --git a/src/ViewModels/StashChanges.cs b/src/ViewModels/StashChanges.cs index 48c9478e..fd937bdd 100644 --- a/src/ViewModels/StashChanges.cs +++ b/src/ViewModels/StashChanges.cs @@ -76,14 +76,11 @@ namespace SourceGit.ViewModels } else { - if (IncludeUntracked) - AddUntracked(_changes); - succ = StashWithChanges(_changes); + succ = new Commands.Stash(_repo.FullPath).Push(Message, IncludeUntracked, KeepIndex); } } else { - AddUntracked(_changes); succ = StashWithChanges(_changes); } @@ -97,40 +94,6 @@ namespace SourceGit.ViewModels }); } - private void AddUntracked(List changes) - { - var toBeAdded = new List(); - foreach (var c in changes) - { - if (c.WorkTree == Models.ChangeState.Added || c.WorkTree == Models.ChangeState.Untracked) - toBeAdded.Add(c); - } - - if (toBeAdded.Count == 0) - return; - - if (Native.OS.GitVersion >= Models.GitVersions.ADD_WITH_PATHSPECFILE) - { - var paths = new List(); - foreach (var c in toBeAdded) - paths.Add(c.Path); - - var tmpFile = Path.GetTempFileName(); - File.WriteAllLines(tmpFile, paths); - new Commands.Add(_repo.FullPath, tmpFile).Exec(); - File.Delete(tmpFile); - } - else - { - for (int i = 0; i < toBeAdded.Count; i += 10) - { - var count = Math.Min(10, toBeAdded.Count - i); - var step = toBeAdded.GetRange(i, count); - new Commands.Add(_repo.FullPath, step).Exec(); - } - } - } - private bool StashWithChanges(List changes) { if (changes.Count == 0) diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index 3ddb2b65..e5755a91 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -57,8 +57,25 @@ namespace SourceGit.ViewModels { Task.Run(() => { - var changes = new Commands.QueryStashChanges(_repo.FullPath, value.SHA).Result(); - Dispatcher.UIThread.Invoke(() => Changes = changes); + var changes = new Commands.CompareRevisions(_repo.FullPath, $"{value.SHA}^", value.SHA).Result(); + var untracked = new HashSet(); + if (value.HasUntracked) + { + var untrackedChanges = new Commands.CompareRevisions(_repo.FullPath, "4b825dc642cb6eb9a060e54bf8d69288fbee4904", value.Parents[2]).Result(); + foreach (var c in untrackedChanges) + { + untracked.Add(c.Path); + changes.Add(c); + } + } + + changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); + + Dispatcher.UIThread.Invoke(() => + { + Changes = changes; + _untrackedChanges = untracked; + }); }); } } @@ -84,8 +101,10 @@ namespace SourceGit.ViewModels { if (value == null) DiffContext = null; + else if (_untrackedChanges.Contains(value.Path)) + DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption("4b825dc642cb6eb9a060e54bf8d69288fbee4904", _selectedStash.Parents[2], value), _diffContext); else - DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption($"{_selectedStash.SHA}^", _selectedStash.SHA, value), _diffContext); + DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, value), _diffContext); } } } @@ -254,6 +273,7 @@ namespace SourceGit.ViewModels private List _visibleStashes = new List(); private string _searchFilter = string.Empty; private Models.Stash _selectedStash = null; + private HashSet _untrackedChanges = new HashSet(); private List _changes = null; private Models.Change _selectedChange = null; private DiffContext _diffContext = null; diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index e6d64469..95f7f010 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -83,6 +83,37 @@ namespace SourceGit.ViewModels } } + public void OpenOrInitRepository(string path, RepositoryNode parent, bool bMoveExistedNode) + { + if (!Directory.Exists(path)) + { + if (File.Exists(path)) + path = Path.GetDirectoryName(path); + else + return; + } + + var isBare = new Commands.IsBareRepository(path).Result(); + var repoRoot = path; + if (!isBare) + { + var test = new Commands.QueryRepositoryRootPath(path).ReadToEnd(); + if (!test.IsSuccess || string.IsNullOrEmpty(test.StdOut)) + { + InitRepository(path, parent, test.StdErr); + return; + } + + repoRoot = test.StdOut.Trim(); + } + + var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(repoRoot, parent, bMoveExistedNode); + Refresh(); + + var launcher = App.GetLauncer(); + launcher?.OpenRepositoryInTab(node, launcher.ActivePage); + } + public void InitRepository(string path, RepositoryNode parent, string reason) { if (!Preferences.Instance.IsGitConfigured()) diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index bb083d82..c92a3612 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -19,14 +19,14 @@ - - - - - + + + + + @@ -35,14 +35,14 @@ - - - - - + + + + + diff --git a/src/Views/CreateBranch.axaml b/src/Views/CreateBranch.axaml index 43f4e50c..49bfda8d 100644 --- a/src/Views/CreateBranch.axaml +++ b/src/Views/CreateBranch.axaml @@ -18,8 +18,8 @@ - - + + - - - - - + Text="{DynamicResource Text.CreateBranch.LocalChanges}" + IsVisible="{Binding !IsBareRepository}"/> + + + + + + + + IsChecked="{Binding CheckoutAfterCreated, Mode=TwoWay}" + IsVisible="{Binding !IsBareRepository}"/> diff --git a/src/Views/EnhancedSelectableTextBlock.cs b/src/Views/EnhancedSelectableTextBlock.cs new file mode 100644 index 00000000..183b7021 --- /dev/null +++ b/src/Views/EnhancedSelectableTextBlock.cs @@ -0,0 +1,20 @@ +using System; + +using Avalonia; +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public class EnhancedSelectableTextBlock : SelectableTextBlock + { + protected override Type StyleKeyOverride => typeof(SelectableTextBlock); + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == TextProperty) + UpdateLayout(); + } + } +} diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index 1ce4bb9a..67293b50 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -45,7 +45,7 @@ FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}" Margin="0,0,0,8"/> - + @@ -66,6 +66,9 @@ + + + + + + + + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 39abb71b..832cca80 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -146,6 +146,12 @@ namespace SourceGit.Views return; } + if (e.Key == Key.Q) { + App.Quit(0); + e.Handled = true; + return; + } + if ((OperatingSystem.IsMacOS() && e.KeyModifiers.HasFlag(KeyModifiers.Alt) && e.Key == Key.Right) || (!OperatingSystem.IsMacOS() && !e.KeyModifiers.HasFlag(KeyModifiers.Shift) && e.Key == Key.Tab)) { diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index d928cc20..00f9f6ce 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -40,7 +40,7 @@ - +