1
0

feat: Add merge handling logic.

This commit is contained in:
Ca2didi 2025-04-17 01:06:02 +08:00
parent 852d935e80
commit d3071b4999
6 changed files with 137 additions and 11 deletions

View File

@ -112,7 +112,7 @@ public class RepositoryFileTreeAccessor : IDisposable, IAsyncDisposable, IEnumer
return false;
}
public bool TryWriteDataIntoStream(string workPath, Stream stream, out DateTime modifyTime)
public bool TryWriteDataIntoDisk(string workPath, Stream stream, out DateTime modifyTime)
{
DisposeCheck();
if (stream == null || !stream.CanWrite) throw new ArgumentException("Stream is not writable!");
@ -129,7 +129,7 @@ public class RepositoryFileTreeAccessor : IDisposable, IAsyncDisposable, IEnumer
return false;
}
public bool TryWriteDataIntoStream(string workPath, string destinationPath)
public bool TryWriteDataIntoDisk(string workPath, string destinationPath)
{
DisposeCheck();

View File

@ -5,15 +5,19 @@ using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;
using AvaloniaEdit.Utils;
using Flawless.Abstraction;
using Flawless.Client.Models;
using Flawless.Client.Remote;
using Flawless.Client.ViewModels.ModalBox;
using Flawless.Client.Views.ModalBox;
using Flawless.Core.BinaryDataFormat;
using Newtonsoft.Json;
using Refit;
using Ursa.Controls;
using CommitManifest = Flawless.Core.Modal.CommitManifest;
using DepotLabel = Flawless.Core.Modal.DepotLabel;
using WorkspaceFile = Flawless.Core.Modal.WorkspaceFile;
@ -742,7 +746,7 @@ public class RepositoryService : BaseService<RepositoryService>
// Write into fs
if (directory != null) Directory.CreateDirectory(directory);
if (!accessor.TryWriteDataIntoStream(f.WorkPath, pfs))
if (!accessor.TryWriteDataIntoDisk(f.WorkPath, pfs))
throw new InvalidDataException($"Can not write {f.WorkPath} into repository.");
}
}
@ -789,7 +793,7 @@ public class RepositoryService : BaseService<RepositoryService>
case ChangeInfoType.Remove:
case ChangeInfoType.Modify:
wfs = WorkPath.ToPlatformPath(ci.File.WorkPath, ls.LocalAccessor.WorkingDirectory);
ls.RepoAccessor!.TryWriteDataIntoStream(ci.File.WorkPath, wfs);
ls.RepoAccessor!.TryWriteDataIntoDisk(ci.File.WorkPath, wfs);
break;
case ChangeInfoType.Folder:
@ -848,13 +852,12 @@ public class RepositoryService : BaseService<RepositoryService>
{
if (mergeChanges)
unmerged.Add(f);
else if (resetChanges && !accessor.TryWriteDataIntoStream(f.WorkPath, pfs))
else if (resetChanges && !accessor.TryWriteDataIntoDisk(f.WorkPath, pfs))
throw new InvalidDataException($"Can not write {f.WorkPath} into repository. (Reset changes)");
}
else
{
if (!accessor.TryWriteDataIntoStream(f.WorkPath, pfs))
if (!accessor.TryWriteDataIntoDisk(f.WorkPath, pfs))
throw new InvalidDataException($"Can not write {f.WorkPath} into repository.");
}
}
@ -864,16 +867,16 @@ public class RepositoryService : BaseService<RepositoryService>
{
var tester = accessor.Manifest.FilePaths.Select(static x => x.WorkPath).ToHashSet();
foreach (var f in Directory.GetFiles(ls.LocalAccessor.WorkingDirectory, "*", SearchOption.AllDirectories))
{
{
var wfs = WorkPath.FromPlatformPath(f, ls.LocalAccessor.WorkingDirectory);
if (!tester.Contains(wfs)) File.Delete(f);
}
}
// Handle merge one by one.
foreach (var f in unmerged)
if (unmerged.Count > 0)
{
throw new Exception("No merge tools has been detected! Merge failed.");
await MergeFilesAsync(repo, accessor, unmerged);
}
}
catch (Exception e)
@ -933,6 +936,49 @@ public class RepositoryService : BaseService<RepositoryService>
}
}
public async ValueTask MergeFilesAsync(RepositoryModel repo, RepositoryFileTreeAccessor repoAccessor, IEnumerable<WorkspaceFile> unmerged)
{
var root = PathUtility.GetWorkspaceManagerPath(Api.C.Username.Value, repo.OwnerName, repo.Name);
var srcTem = Path.Combine(root, "MergeInTemp");
using (Disposable.Create(srcTem, r => Directory.Delete(r, true)))
{
Directory.CreateDirectory(root);
// Prepare server files
var wsRoot = PathUtility.GetWorkspacePath(Api.C.Username.Value, repo.OwnerName, repo.Name);
var vm = new MergeDialogViewModel(repo, unmerged, srcTem, wsRoot);
foreach (var f in vm.MergeFiles)
{
var pf = WorkPath.ToPlatformPath(f.WorkPath, vm.SrcFolder);
Directory.CreateDirectory(Path.GetDirectoryName(pf)!);
await using var fs = File.Create(pf);
repoAccessor.TryWriteDataIntoDisk(f.WorkPath, fs, out _);
}
// Raise merge window
var opt = UIHelper.DefaultOverlayDialogOptionsYesNo();
opt.Buttons = DialogButton.OK;
opt.Title = "Merge files";
opt.FullScreen = true;
opt.IsCloseButtonVisible = false;
await OverlayDialog.ShowModal<MergeDialogView, MergeDialogViewModel>(vm, AppDefaultValues.HostId, opt);
// Rollback files
foreach (var f in vm.MergeFiles)
{
var copyFrom = WorkPath.ToPlatformPath(f.WorkPath, vm.TmpFolder);
if (!File.Exists(copyFrom)) continue; // If file existed, override it.
var copyTo = WorkPath.ToPlatformPath(f.WorkPath, vm.DstFolder);
Directory.CreateDirectory(Path.GetDirectoryName(copyTo)!);
File.Copy(copyFrom, copyTo, true);
}
}
}
public async ValueTask<bool> UpdateCommitsHistoryFromServerAsync(RepositoryModel repo)
{
var api = Api.C;

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
using Flawless.Abstraction;
using Flawless.Client.Models;
using Flawless.Core.Modal;
using ReactiveUI.SourceGenerators;
namespace Flawless.Client.ViewModels.ModalBox;
public partial class MergeDialogViewModel : ViewModelBase
{
public ObservableCollection<WorkspaceFile> MergeFiles { get; }
public RepositoryModel Repository { get; }
public string SrcFolder { get; }
public string DstFolder { get; }
public string TmpFolder { get; }
public MergeDialogViewModel(RepositoryModel repository, IEnumerable<WorkspaceFile> files,
string srcFolder, string dstFolder)
{
Repository = repository;
MergeFiles = new ObservableCollection<WorkspaceFile>(files);
SrcFolder = srcFolder;
DstFolder = dstFolder;
TmpFolder = Directory.CreateTempSubdirectory("Flawless_Merge").Name;
}
[ReactiveCommand]
private async Task RaiseMergeToolsAsync(string fileWorkPath)
{
var srcFile = WorkPath.ToPlatformPath(fileWorkPath, SrcFolder);
var dstFile = Path.Combine(fileWorkPath, DstFolder);
var tmpFile = Path.Combine(fileWorkPath, TmpFolder);
}
}

View File

@ -479,7 +479,7 @@ public partial class RepositoryViewModel : RoutableViewModelBase
var kid = Repository.Commits.MaxBy(k => k.CommittedOn)!.CommitId;
var changeDict = changes.ToImmutableDictionary(x => x.File.WorkPath, x => x.File);
await RepositoryService.C.SetPointerAndMergeDepotsWithLocalFromRemoteAsync(
Repository, kid, changeDict, RepositoryResetMethod.Merge);
Repository, kid, changeDict, RepositoryResetMethod.Keep);
await DetectLocalChangesAsyncCommand.Execute();
await RendererFileTreeAsync();

View File

@ -0,0 +1,23 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Flawless.Client.ViewModels.ModalBox"
xmlns:u="https://irihi.tech/ursa"
x:DataType="vm:MergeDialogViewModel"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Flawless.Client.Views.ModalBox.MergeDialogView">
<ListBox ItemsSource="{Binding MergeFiles}">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<Grid Column="Auto, *, Auto">
<Label Grid.Column="0" Content="{Binding WorkPath}"/>
<u:IconButton Command="{Binding $parent[ItemsControl].((vm:MergeDialogViewModel)DataContext).RaiseMergeToolsCommand}"
CommandParameter="{Binding WorkPath}"/>
</Grid>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>

View File

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Flawless.Client.Views.ModalBox;
public partial class MergeDialogView : UserControl
{
public MergeDialogView()
{
InitializeComponent();
}
}