Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature: Added support for local branch checkout #12316

Merged
merged 21 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 53 additions & 8 deletions src/Files.App/Helpers/DynamicDialogFactory.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.WinUI;
using Files.App.Dialogs;
using Files.App.Extensions;
using Files.App.Filesystem;
using Files.App.ViewModels.Dialogs;
using Files.Shared.Enums;
using Files.Shared.Extensions;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.System;

namespace Files.App.Helpers
Expand Down Expand Up @@ -224,5 +216,58 @@ public static DynamicDialog GetFor_CredentialEntryDialog(string path)

return dialog;
}

public static DynamicDialog GetFor_GitCheckoutConflicts()
{
DynamicDialog dialog = null!;

var optionsListView = new ListView()
{
ItemsSource = new string[]
{
"BringChanges".GetLocalizedResource(),
"StashChanges".GetLocalizedResource(),
"DiscardChanges".GetLocalizedResource()
},
SelectionMode = ListViewSelectionMode.Single
};
optionsListView.SelectedIndex = 0;

optionsListView.SelectionChanged += (listView, args) =>
{
dialog.ViewModel.AdditionalData = (GitCheckoutOptions)optionsListView.SelectedIndex;
};

dialog = new DynamicDialog(new DynamicDialogViewModel()
{
TitleText = "UncommitedChanges".GetLocalizedResource(),
PrimaryButtonText = "OK".GetLocalizedResource(),
CloseButtonText = "Cancel".GetLocalizedResource(),
SubtitleText = "UncommitedChangesMessage".GetLocalizedResource(),
DisplayControl = new Grid()
{
MinWidth = 250d,
Children =
{
new StackPanel()
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
{
Spacing = 10d,
Children =
{
optionsListView
}
}
}
},
AdditionalData = GitCheckoutOptions.BringChanges,
CloseButtonAction = (vm, e) =>
{
dialog.ViewModel.AdditionalData = GitCheckoutOptions.None;
vm.HideDialog();
}
});

return dialog;
}
}
}
57 changes: 57 additions & 0 deletions src/Files.App/Helpers/GitHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,62 @@ public static class GitHelpers
return null;
}
}

public static string[] GetLocalBranchesNames(string? path)
{
if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path))
return Array.Empty<string>();

using var repository = new Repository(path);
return repository.Branches
.Where(b => !b.IsRemote)
.Select(b => b.FriendlyName)
.ToArray();
}

public static async Task Checkout(string? repositoryPath, string? branch)
{
if (string.IsNullOrWhiteSpace(repositoryPath) || !Repository.IsValid(repositoryPath))
return;

using var repository = new Repository(repositoryPath);
var checkoutBranch = repository.Branches[branch];
if (checkoutBranch is null)
return;

var options = new CheckoutOptions();
var isBringingChanges = false;

if (repository.RetrieveStatus().IsDirty)
{
var dialog = DynamicDialogFactory.GetFor_GitCheckoutConflicts();
await dialog.ShowAsync();

var resolveConflictOption = (GitCheckoutOptions)dialog.ViewModel.AdditionalData;

switch (resolveConflictOption)
{
case GitCheckoutOptions.None:
return;
case GitCheckoutOptions.DiscardChanges:
options.CheckoutModifiers = CheckoutModifiers.Force;
break;
case GitCheckoutOptions.BringChanges:
case GitCheckoutOptions.StashChanges:
repository.Stashes.Add(repository.Config.BuildSignature(DateTimeOffset.Now));

isBringingChanges = resolveConflictOption is GitCheckoutOptions.BringChanges;
break;
}
}

LibGit2Sharp.Commands.Checkout(repository, checkoutBranch, options);

if (isBringingChanges)
{
var lastStashIndex = repository.Stashes.Count() - 1;
repository.Stashes.Pop(lastStashIndex, new StashApplyOptions());
}
}
}
}
18 changes: 18 additions & 0 deletions src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -3262,4 +3262,22 @@
<data name="SecurityUnableToDisplayPermissions" xml:space="preserve">
<value>Unable to display permissions.</value>
</data>
<data name="StashChanges" xml:space="preserve">
<value>Stash changes</value>
</data>
<data name="DiscardChanges" xml:space="preserve">
<value>Discard changes</value>
</data>
<data name="BringChanges" xml:space="preserve">
<value>Bring changes</value>
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="UncommitedChanges" xml:space="preserve">
<value>There are uncommited changes on the local branch</value>
</data>
<data name="UncommitedChangesMessage" xml:space="preserve">
<value>What do you want to do with them?</value>
</data>
<data name="Branches" xml:space="preserve">
<value>Branches:</value>
</data>
</root>
32 changes: 30 additions & 2 deletions src/Files.App/UserControls/StatusBarControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:Files.App.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helpers="using:Files.App.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="32"
d:DesignWidth="400"
Expand Down Expand Up @@ -53,10 +54,37 @@
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="8">
<TextBlock
<Button
x:Name="GitBranch"
x:Load="{x:Bind DirectoryPropertiesViewModel.GitBranchDisplayName, Mode=OneWay, Converter={StaticResource NullToFalseConverter}}"
Text="{x:Bind DirectoryPropertiesViewModel.GitBranchDisplayName, Mode=OneWay}" />
Background="Transparent"
BorderThickness="0"
Content="{x:Bind DirectoryPropertiesViewModel.GitBranchDisplayName, Mode=OneWay}">
<Button.Flyout>
<Flyout Opening="Flyout_Opening">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="8" />
<Setter Property="CornerRadius" Value="{StaticResource OverlayCornerRadius}" />
</Style>
</Flyout.FlyoutPresenterStyle>
<StackPanel
Width="200"
Height="200"
LostFocus="StackPanel_LostFocus"
Spacing="8">
<TextBlock
Margin="16,0,0,0"
FontWeight="Bold"
Text="{helpers:ResourceString Name=Branches}" />
<ListView
ItemsSource="{x:Bind DirectoryPropertiesViewModel.BranchesNames, Mode=OneWay}"
SelectedIndex="{x:Bind DirectoryPropertiesViewModel.SelectedBranchIndex, Mode=TwoWay}"
SelectionMode="Single" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</Grid>
</UserControl>
11 changes: 11 additions & 0 deletions src/Files.App/UserControls/StatusBarControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Files.App.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;

namespace Files.App.UserControls
{
Expand Down Expand Up @@ -42,5 +43,15 @@ public StatusBarControl()
{
InitializeComponent();
}

private void Flyout_Opening(object sender, object e)
{
DirectoryPropertiesViewModel.SelectedBranchIndex = DirectoryPropertiesViewModel.ActiveBranchIndex;
}

private void StackPanel_LostFocus(object sender, RoutedEventArgs e)
{
((Popup)((FlyoutPresenter)((StackPanel)sender).Parent).Parent).IsOpen = false;
}
}
}
43 changes: 37 additions & 6 deletions src/Files.App/ViewModels/DirectoryPropertiesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,49 @@ namespace Files.App.ViewModels
{
public class DirectoryPropertiesViewModel : ObservableObject
{
private string directoryItemCount;
public int ActiveBranchIndex { get; private set; }

private string _DirectoryItemCount;
public string DirectoryItemCount
{
get => directoryItemCount;
set => SetProperty(ref directoryItemCount, value);
get => _DirectoryItemCount;
set => SetProperty(ref _DirectoryItemCount, value);
}

private string? gitBranchDisplayName;
private string? _GitBranchDisplayName;
public string? GitBranchDisplayName
{
get => gitBranchDisplayName;
set => SetProperty(ref gitBranchDisplayName, value);
get => _GitBranchDisplayName;
private set => SetProperty(ref _GitBranchDisplayName, value);
}

private int _SelectedBranchIndex;
public int SelectedBranchIndex
{
get => _SelectedBranchIndex;
set
{
if (SetProperty(ref _SelectedBranchIndex, value) && value != -1 && value != ActiveBranchIndex)
CheckoutRequested?.Invoke(this, BranchesNames[value]);
}
}

public ObservableCollection<string> BranchesNames { get; } = new();

public EventHandler<string>? CheckoutRequested;

public void UpdateGitInfo(bool isGitRepository, string activeBranch, string[] branches)
{
GitBranchDisplayName = isGitRepository
? string.Format("Branch".GetLocalizedResource(), activeBranch)
: null;

BranchesNames.Clear();
foreach (var name in branches)
BranchesNames.Add(name);

ActiveBranchIndex = BranchesNames.IndexOf(activeBranch);
SelectedBranchIndex = ActiveBranchIndex;
}
}
}
23 changes: 17 additions & 6 deletions src/Files.App/Views/Shells/BaseShellPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@ public BaseLayout ContentPage
{
if (value != _ContentPage)
{
if (_ContentPage is not null)
_ContentPage.DirectoryPropertiesViewModel.CheckoutRequested -= GitCheckout_Required;

_ContentPage = value;

NotifyPropertyChanged(nameof(ContentPage));
NotifyPropertyChanged(nameof(SlimContentPage));
_ContentPage.DirectoryPropertiesViewModel.CheckoutRequested += GitCheckout_Required;
}
}
}
Expand Down Expand Up @@ -219,9 +223,10 @@ protected void FilesystemViewModel_DirectoryInfoUpdated(object sender, EventArgs

InstanceViewModel.GitRepositoryPath = FilesystemViewModel.GitDirectory;

ContentPage.DirectoryPropertiesViewModel.GitBranchDisplayName = InstanceViewModel.IsGitRepository
? string.Format("Branch".GetLocalizedResource(), InstanceViewModel.GitBranchName)
: null;
ContentPage.DirectoryPropertiesViewModel.UpdateGitInfo(
InstanceViewModel.IsGitRepository,
InstanceViewModel.GitBranchName,
GitHelpers.GetLocalBranchesNames(InstanceViewModel.GitRepositoryPath));

ContentPage.DirectoryPropertiesViewModel.DirectoryItemCount = $"{FilesystemViewModel.FilesAndFolders.Count} {directoryItemCountLocalization}";
ContentPage.UpdateSelectionSize();
Expand All @@ -230,9 +235,15 @@ protected void FilesystemViewModel_DirectoryInfoUpdated(object sender, EventArgs
protected void FilesystemViewModel_GitDirectoryUpdated(object sender, EventArgs e)
{
InstanceViewModel.UpdateCurrentBranchName();
ContentPage.DirectoryPropertiesViewModel.GitBranchDisplayName = InstanceViewModel.IsGitRepository
? string.Format("Branch".GetLocalizedResource(), InstanceViewModel.GitBranchName)
: null;
ContentPage.DirectoryPropertiesViewModel.UpdateGitInfo(
InstanceViewModel.IsGitRepository,
InstanceViewModel.GitBranchName,
GitHelpers.GetLocalBranchesNames(InstanceViewModel.GitRepositoryPath));
}

protected async void GitCheckout_Required(object? sender, string branchName)
{
await GitHelpers.Checkout(FilesystemViewModel.GitDirectory, branchName);
}

protected virtual void Page_Loaded(object sender, RoutedEventArgs e)
Expand Down
10 changes: 10 additions & 0 deletions src/Files.Backend/Enums/GitCheckoutOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Files.Backend.Enums
{
public enum GitCheckoutOptions
{
BringChanges,
StashChanges,
DiscardChanges,
None
}
}