From 7391c3f1a1734753d8330a28623910128f9fbf8d Mon Sep 17 00:00:00 2001 From: jernejp21 Date: Tue, 21 Apr 2026 13:47:36 +0200 Subject: [PATCH 1/2] Add git init menu Added menu for git init command. Copied cs files from clone, so the functionality still isn't working. --- src/Resources/Locales/en_US.axaml | 10 ++ src/ViewModels/InitGit.cs | 188 ++++++++++++++++++++++++++++++ src/ViewModels/Welcome.cs | 13 +++ src/Views/InitGit.axaml | 103 ++++++++++++++++ src/Views/InitGit.axaml.cs | 46 ++++++++ src/Views/WelcomeToolbar.axaml | 4 + 6 files changed, 364 insertions(+) create mode 100644 src/ViewModels/InitGit.cs create mode 100644 src/Views/InitGit.axaml create mode 100644 src/Views/InitGit.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index d5843d79f..fb63078ad 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -540,6 +540,15 @@ Do you want to run `git init` command under this path? Open repository failed. Reason: Path: + Initialize Git Repository + Create Bare Repository + Bookmark: + Group: + Initial Branch Name + main + Parent Folder + Project Name + my_git_project Cherry-Pick in progress. Processing commit Merge in progress. @@ -945,6 +954,7 @@ Delete DRAG & DROP FOLDER SUPPORTED. CUSTOM GROUPING SUPPORTED. Edit + Initialize Git Repository Move to Another Group Open All Repositories Open Repository diff --git a/src/ViewModels/InitGit.cs b/src/ViewModels/InitGit.cs new file mode 100644 index 000000000..ac0db122e --- /dev/null +++ b/src/ViewModels/InitGit.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class InitGit : Popup + { + [Required(ErrorMessage = "Parent folder is required")] + [CustomValidation(typeof(InitGit), nameof(ValidateParentFolder))] + public string ParentFolder + { + get => _parentFolder; + set => SetProperty(ref _parentFolder, value, true); + } + + public string ProjectName + { + get => _projectName; + set => SetProperty(ref _projectName, value); + } + + public List Groups + { + get; + } + + public RepositoryNode SelectedGroup + { + get => _selectedGroup; + set => SetProperty(ref _selectedGroup, value); + } + + public List Bookmarks + { + get; + } + + public int Bookmark + { + get => _bookmark; + set => SetProperty(ref _bookmark, value); + } + + public string InitialBranch + { + get => _initialBranch; + set => SetProperty(ref _initialBranch, value); + } + + public bool BareRepo + { + get; + set; + } = false; + + public InitGit(string pageId) + { + _pageId = pageId; + + Groups = new List(); + Groups.Add(new RepositoryNode { Name = "No Group (Uncategorized)", Id = string.Empty }); + SelectedGroup = Groups[0]; + CollectGroups(Groups, Preferences.Instance.RepositoryNodes); + + Bookmarks = new List(); + for (var i = 0; i < Models.Bookmarks.Brushes.Length; i++) + Bookmarks.Add(i); + + var activeWorkspace = Preferences.Instance.GetActiveWorkspace(); + _parentFolder = activeWorkspace?.DefaultCloneDir; + if (string.IsNullOrEmpty(ParentFolder)) + _parentFolder = Preferences.Instance.GitDefaultCloneDir; + } + + public static ValidationResult ValidateRemote(string remote, ValidationContext _) + { + if (!Models.Remote.IsValidURL(remote)) + return new ValidationResult("Invalid remote repository URL format"); + return ValidationResult.Success; + } + + public static ValidationResult ValidateParentFolder(string folder, ValidationContext _) + { + if (!Directory.Exists(folder)) + return new ValidationResult("Given path can NOT be found"); + return ValidationResult.Success; + } + + public override async Task Sure() + { + ProgressDescription = "Clone ..."; + + var log = new CommandLog("Clone"); + Use(log); + + var succ = await new Commands.Clone(_pageId, _parentFolder, _remote, _projectName, _useSSH ? _sshKey : "", _initialBranch) + .Use(log) + .ExecAsync(); + if (!succ) + return false; + + var path = _parentFolder; + if (!string.IsNullOrEmpty(_projectName)) + { + path = Path.GetFullPath(Path.Combine(path, _projectName)); + } + else + { + var name = Path.GetFileName(_remote)!; + if (name.EndsWith(".git", StringComparison.Ordinal)) + name = name.Substring(0, name.Length - 4); + else if (name.EndsWith(".bundle", StringComparison.Ordinal)) + name = name.Substring(0, name.Length - 7); + + path = Path.GetFullPath(Path.Combine(path, name)); + } + + if (!Directory.Exists(path)) + { + Models.Notification.Send(_pageId, $"Folder '{path}' can NOT be found", true); + return false; + } + + if (_useSSH && !string.IsNullOrEmpty(_sshKey)) + { + await new Commands.Config(path) + .Use(log) + .SetAsync("remote.origin.sshkey", _sshKey); + } + + if (BareRepo) + { + var submodules = await new Commands.QueryUpdatableSubmodules(path, true).GetResultAsync(); + if (submodules.Count > 0) + await new Commands.Submodule(path) + .Use(log) + .UpdateAsync(submodules, true); + } + + log.Complete(); + + var parent = _selectedGroup is { Id: not "" } ? _selectedGroup : null; + var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, parent, true); + node.Bookmark = _bookmark; + await node.UpdateStatusAsync(false, null); + + var launcher = App.GetLauncher(); + LauncherPage page = null; + foreach (var one in launcher.Pages) + { + if (one.Node.Id == _pageId) + { + page = one; + break; + } + } + + Welcome.Instance.Refresh(); + launcher.OpenRepositoryInTab(node, page); + return true; + } + + private void CollectGroups(List outs, List collections) + { + foreach (var node in collections) + { + if (!node.IsRepository) + { + outs.Add(node); + CollectGroups(outs, node.SubNodes); + } + } + } + + private string _pageId = string.Empty; + private string _remote = string.Empty; + private bool _useSSH = false; + private string _sshKey = string.Empty; + private string _parentFolder = string.Empty; + private string _projectName = string.Empty; + private string _initialBranch = string.Empty; + private RepositoryNode _selectedGroup = null; + private int _bookmark = 0; + } +} diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index 49539292d..2d052d314 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -171,6 +171,19 @@ public void OpenTerminal() Native.OS.OpenTerminal(null); } + public void InitGit() + { + if (!Preferences.Instance.IsGitConfigured()) + { + Models.Notification.Send(null, App.Text("NotConfigured"), true); + return; + } + + var activePage = App.GetLauncher().ActivePage; + if (activePage != null && activePage.CanCreatePopup()) + activePage.Popup = new InitGit(activePage.Node.Id); + } + public void ScanDefaultCloneDir() { if (!Preferences.Instance.IsGitConfigured()) diff --git a/src/Views/InitGit.axaml b/src/Views/InitGit.axaml new file mode 100644 index 000000000..e0262ba4a --- /dev/null +++ b/src/Views/InitGit.axaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/InitGit.axaml.cs b/src/Views/InitGit.axaml.cs new file mode 100644 index 000000000..21e3ce84a --- /dev/null +++ b/src/Views/InitGit.axaml.cs @@ -0,0 +1,46 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input.Platform; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; + +namespace SourceGit.Views +{ + public partial class InitGit : UserControl + { + public InitGit() + { + InitializeComponent(); + } + + protected override async void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + } + + private async void SelectParentFolder(object _, RoutedEventArgs e) + { + var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + var toplevel = TopLevel.GetTopLevel(this); + if (toplevel == null) + return; + + try + { + var selected = await toplevel.StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) + { + var folder = selected[0]; + var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder?.Path.ToString(); + TxtParentFolder.Text = folderPath; + } + } + catch (Exception exception) + { + Models.Notification.Send(null, $"Failed to select parent folder: {exception.Message}", true); + } + + e.Handled = true; + } + } +} diff --git a/src/Views/WelcomeToolbar.axaml b/src/Views/WelcomeToolbar.axaml index 91f2064fa..ef662fa1b 100644 --- a/src/Views/WelcomeToolbar.axaml +++ b/src/Views/WelcomeToolbar.axaml @@ -33,6 +33,10 @@ + + From 313f9f639de475ff5d6aa6f68d048af174a67a6a Mon Sep 17 00:00:00 2001 From: jernejp21 Date: Thu, 23 Apr 2026 14:18:38 +0200 Subject: [PATCH 2/2] Implement logic for git init Implemented logic for git init command. Can also init bare repository. Signed-off-by: jernejp21 --- src/Commands/InitGit.cs | 28 ++++++++++++++++++ src/Resources/Locales/en_US.axaml | 2 +- src/ViewModels/InitGit.cs | 49 ++++++------------------------- 3 files changed, 38 insertions(+), 41 deletions(-) create mode 100644 src/Commands/InitGit.cs diff --git a/src/Commands/InitGit.cs b/src/Commands/InitGit.cs new file mode 100644 index 000000000..58df0db78 --- /dev/null +++ b/src/Commands/InitGit.cs @@ -0,0 +1,28 @@ +using System.Text; + +namespace SourceGit.Commands +{ + public class InitGit : Command + { + public InitGit(string ctx, string path, string localName, string branchName, bool bareRepo) + { + Context = ctx; + WorkingDirectory = path; + + var builder = new StringBuilder(1024); + builder.Append("init "); + + if (bareRepo) + builder.Append("--bare "); + else + { + if (!string.IsNullOrEmpty(branchName)) + builder.Append("-b " + branchName + " "); + } + + builder.Append(localName.Quoted()); + + Args = builder.ToString(); + } + } +} diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index fb63078ad..9f2217db5 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -548,7 +548,7 @@ main Parent Folder Project Name - my_git_project + new_git_project Cherry-Pick in progress. Processing commit Merge in progress. diff --git a/src/ViewModels/InitGit.cs b/src/ViewModels/InitGit.cs index ac0db122e..b3ad5d257 100644 --- a/src/ViewModels/InitGit.cs +++ b/src/ViewModels/InitGit.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; +using System.Globalization; namespace SourceGit.ViewModels { @@ -18,7 +19,7 @@ public string ParentFolder public string ProjectName { - get => _projectName; + get => null; set => SetProperty(ref _projectName, value); } @@ -46,7 +47,7 @@ public int Bookmark public string InitialBranch { - get => _initialBranch; + get => null; set => SetProperty(ref _initialBranch, value); } @@ -75,13 +76,6 @@ public InitGit(string pageId) _parentFolder = Preferences.Instance.GitDefaultCloneDir; } - public static ValidationResult ValidateRemote(string remote, ValidationContext _) - { - if (!Models.Remote.IsValidURL(remote)) - return new ValidationResult("Invalid remote repository URL format"); - return ValidationResult.Success; - } - public static ValidationResult ValidateParentFolder(string folder, ValidationContext _) { if (!Directory.Exists(folder)) @@ -91,12 +85,12 @@ public static ValidationResult ValidateParentFolder(string folder, ValidationCon public override async Task Sure() { - ProgressDescription = "Clone ..."; + ProgressDescription = "Init ..."; - var log = new CommandLog("Clone"); + var log = new CommandLog("Init"); Use(log); - var succ = await new Commands.Clone(_pageId, _parentFolder, _remote, _projectName, _useSSH ? _sshKey : "", _initialBranch) + var succ = await new Commands.InitGit(_pageId, _parentFolder, _projectName, _initialBranch, BareRepo) .Use(log) .ExecAsync(); if (!succ) @@ -109,13 +103,7 @@ public override async Task Sure() } else { - var name = Path.GetFileName(_remote)!; - if (name.EndsWith(".git", StringComparison.Ordinal)) - name = name.Substring(0, name.Length - 4); - else if (name.EndsWith(".bundle", StringComparison.Ordinal)) - name = name.Substring(0, name.Length - 7); - - path = Path.GetFullPath(Path.Combine(path, name)); + path = Path.GetFullPath(Path.Combine(path, "new_git_project")); } if (!Directory.Exists(path)) @@ -124,22 +112,6 @@ public override async Task Sure() return false; } - if (_useSSH && !string.IsNullOrEmpty(_sshKey)) - { - await new Commands.Config(path) - .Use(log) - .SetAsync("remote.origin.sshkey", _sshKey); - } - - if (BareRepo) - { - var submodules = await new Commands.QueryUpdatableSubmodules(path, true).GetResultAsync(); - if (submodules.Count > 0) - await new Commands.Submodule(path) - .Use(log) - .UpdateAsync(submodules, true); - } - log.Complete(); var parent = _selectedGroup is { Id: not "" } ? _selectedGroup : null; @@ -176,12 +148,9 @@ private void CollectGroups(List outs, List colle } private string _pageId = string.Empty; - private string _remote = string.Empty; - private bool _useSSH = false; - private string _sshKey = string.Empty; private string _parentFolder = string.Empty; - private string _projectName = string.Empty; - private string _initialBranch = string.Empty; + private string _projectName = App.Text("InitGit.ProjectName.Placeholder"); + private string _initialBranch = App.Text("InitGit.InitialBranch.Placeholder"); private RepositoryNode _selectedGroup = null; private int _bookmark = 0; }