From b519ead5a1850618dfa3c89dfadd1d55ee4a1ba1 Mon Sep 17 00:00:00 2001 From: leo Date: Mon, 23 Jun 2025 17:22:57 +0800 Subject: [PATCH] feature: supports to add ignored file(s) locally (saved in `$GIT_DIR/info/exclude`) (#1447) Signed-off-by: leo --- src/Commands/GitIgnore.cs | 23 ----------- src/Models/GitIgnoreFile.cs | 24 ++++++++++++ src/Resources/Locales/en_US.axaml | 3 ++ src/Resources/Locales/zh_CN.axaml | 3 ++ src/Resources/Locales/zh_TW.axaml | 3 ++ src/ViewModels/AddToIgnore.cs | 64 +++++++++++++++++++++++++++++++ src/ViewModels/WorkingCopy.cs | 18 ++++++--- src/Views/AddToIgnore.axaml | 47 +++++++++++++++++++++++ src/Views/AddToIgnore.axaml.cs | 12 ++++++ 9 files changed, 168 insertions(+), 29 deletions(-) delete mode 100644 src/Commands/GitIgnore.cs create mode 100644 src/Models/GitIgnoreFile.cs create mode 100644 src/ViewModels/AddToIgnore.cs create mode 100644 src/Views/AddToIgnore.axaml create mode 100644 src/Views/AddToIgnore.axaml.cs diff --git a/src/Commands/GitIgnore.cs b/src/Commands/GitIgnore.cs deleted file mode 100644 index 8b351f5e..00000000 --- a/src/Commands/GitIgnore.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.IO; - -namespace SourceGit.Commands -{ - public static class GitIgnore - { - public static void Add(string repo, string pattern) - { - var file = Path.Combine(repo, ".gitignore"); - if (!File.Exists(file)) - { - File.WriteAllLines(file, [pattern]); - return; - } - - var org = File.ReadAllText(file); - if (!org.EndsWith('\n')) - File.AppendAllLines(file, ["", pattern]); - else - File.AppendAllLines(file, [pattern]); - } - } -} diff --git a/src/Models/GitIgnoreFile.cs b/src/Models/GitIgnoreFile.cs new file mode 100644 index 00000000..61345021 --- /dev/null +++ b/src/Models/GitIgnoreFile.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.IO; + +namespace SourceGit.Models +{ + public class GitIgnoreFile + { + public static readonly List Supported = [new(true), new(false)]; + + public bool IsShared { get; set; } + public string File => IsShared ? ".gitignore" : "$GIT_DIR/info/exclude"; + public string Desc => IsShared ? "Shared" : "Private"; + + public GitIgnoreFile(bool isShared) + { + IsShared = isShared; + } + + public string GetFullPath(string repoPath, string gitDir) + { + return IsShared ? Path.Combine(repoPath, ".gitignore") : Path.Combine(gitDir, "info", "exclude"); + } + } +} diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index d4b4d26f..0817ed45 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -2,6 +2,9 @@ About About SourceGit Opensource & Free Git GUI Client + Add File(s) To Ignore + Pattern: + Storage File: Add Worktree Location: Path for this worktree. Relative path is supported. diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 2a5991fd..d33ff993 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -6,6 +6,9 @@ 关于软件 关于本软件 开源免费的Git客户端 + 新增忽略文件 + 匹配模式 : + 保存位置 : 新增工作树 工作树路径 : 填写该工作树的路径。支持相对路径。 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index f1736ff8..dec6eddd 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -6,6 +6,9 @@ 關於 關於 SourceGit 開源免費的 Git 客戶端 + 新增忽略檔案 + 匹配模式 : + 儲存路徑 : 新增工作區 工作區路徑: 填寫該工作區的路徑。支援相對路徑。 diff --git a/src/ViewModels/AddToIgnore.cs b/src/ViewModels/AddToIgnore.cs new file mode 100644 index 00000000..74f7e3ec --- /dev/null +++ b/src/ViewModels/AddToIgnore.cs @@ -0,0 +1,64 @@ +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class AddToIgnore : Popup + { + [Required(ErrorMessage = "Ignore pattern is required!")] + public string Pattern + { + get => _pattern; + set => SetProperty(ref _pattern, value, true); + } + + [Required(ErrorMessage = "Storage file is required!!!")] + public Models.GitIgnoreFile StorageFile + { + get; + set; + } + + public AddToIgnore(Repository repo, string pattern) + { + _repo = repo; + _pattern = pattern; + StorageFile = Models.GitIgnoreFile.Supported[0]; + } + + public override Task Sure() + { + _repo.SetWatcherEnabled(false); + ProgressDescription = "Adding Ignored File(s) ..."; + + return Task.Run(() => + { + var file = StorageFile.GetFullPath(_repo.FullPath, _repo.GitDir); + if (!File.Exists(file)) + { + File.WriteAllLines(file, [_pattern]); + } + else + { + var org = File.ReadAllText(file); + if (!org.EndsWith('\n')) + File.AppendAllLines(file, ["", _pattern]); + else + File.AppendAllLines(file, [_pattern]); + } + + CallUIThread(() => + { + _repo.MarkWorkingCopyDirtyManually(); + _repo.SetWatcherEnabled(true); + }); + + return true; + }); + } + + private readonly Repository _repo; + private string _pattern; + } +} diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index f41bc162..be9e1a7d 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -771,7 +771,8 @@ namespace SourceGit.ViewModels ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder"); ignoreFolder.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, $"{selectedSingleFolder}/"); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new AddToIgnore(_repo, $"{selectedSingleFolder}/")); e.Handled = true; }; addToIgnore.Items.Add(ignoreFolder); @@ -783,7 +784,8 @@ namespace SourceGit.ViewModels singleFile.Header = App.Text("WorkingCopy.AddToGitIgnore.SingleFile"); singleFile.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, change.Path); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new AddToIgnore(_repo, change.Path)); e.Handled = true; }; addToIgnore.Items.Add(singleFile); @@ -794,7 +796,8 @@ namespace SourceGit.ViewModels byExtension.Header = App.Text("WorkingCopy.AddToGitIgnore.Extension", extension); byExtension.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, $"*{extension}"); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new AddToIgnore(_repo, $"*{extension}")); e.Handled = true; }; addToIgnore.Items.Add(byExtension); @@ -805,7 +808,8 @@ namespace SourceGit.ViewModels byExtensionInSameFolder.Click += (_, e) => { var dir = Path.GetDirectoryName(change.Path)!.Replace('\\', '/').TrimEnd('/'); - Commands.GitIgnore.Add(_repo.FullPath, $"{dir}/*{extension}"); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new AddToIgnore(_repo, $"{dir}/*{extension}")); e.Handled = true; }; addToIgnore.Items.Add(byExtensionInSameFolder); @@ -827,7 +831,8 @@ namespace SourceGit.ViewModels ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder"); ignoreFolder.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, $"{selectedSingleFolder}/"); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new AddToIgnore(_repo, $"{selectedSingleFolder}/")); e.Handled = true; }; addToIgnore.Items.Add(ignoreFolder); @@ -1133,7 +1138,8 @@ namespace SourceGit.ViewModels ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder"); ignoreFolder.Click += (_, e) => { - Commands.GitIgnore.Add(_repo.FullPath, $"{selectedSingleFolder}/"); + if (_repo.CanCreatePopup()) + _repo.ShowPopup(new AddToIgnore(_repo, $"{selectedSingleFolder}/")); e.Handled = true; }; diff --git a/src/Views/AddToIgnore.axaml b/src/Views/AddToIgnore.axaml new file mode 100644 index 00000000..9af53f58 --- /dev/null +++ b/src/Views/AddToIgnore.axaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/AddToIgnore.axaml.cs b/src/Views/AddToIgnore.axaml.cs new file mode 100644 index 00000000..ae1745e6 --- /dev/null +++ b/src/Views/AddToIgnore.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class AddToIgnore : UserControl + { + public AddToIgnore() + { + InitializeComponent(); + } + } +}