Skip to content

[wip] Integrated merge tool #988

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

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions src/ViewModels/Conflict.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,21 @@ public bool IsResolved
private set;
}

public Models.Change Change
{
get => _change;
}

public Repository Repository
{
get => _repo;
}

public Conflict(Repository repo, WorkingCopy wc, Models.Change change)
{
_wc = wc;
_change = change;
_repo = repo;

IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).ReadToEnd().IsSuccess;

Expand Down Expand Up @@ -98,5 +109,6 @@ public void OpenExternalMergeTool()

private WorkingCopy _wc = null;
private Models.Change _change = null;
private Repository _repo = null;
}
}
108 changes: 108 additions & 0 deletions src/Views/TextDiffView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

using Avalonia;
Expand Down Expand Up @@ -2016,4 +2017,111 @@ private void OnDiscardChunk(object _1, RoutedEventArgs _2)
repo.SetWatcherEnabled(true);
}
}

public class ConflictTextDiffPresenter: SingleSideTextDiffPresenter
{
public enum Side : int
{
Ours = 0, WorkingCopy = 1, Theirs = 2
}

public Side DisplaySide {
get; set;
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
}
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
}

protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);

if (DataContext is ViewModels.Conflict diff)
{
string conflict_start_marker = "<<<<<<< HEAD";
string conflict_separator_marker = "=======";
string conflict_end_marker = ">>>>>>> ";

var cmd = new Commands.QueryRefsContainsCommit(diff.Repository.FullPath, (diff.Theirs as Models.Commit)?.SHA);
var their_branches = cmd.Result().ToArray();

string filePath = Path.Combine(diff.Repository.FullPath, diff.Change.Path);
var lines =File.ReadAllLines(filePath).ToList<string>();

bool shouldFilter = DisplaySide != Side.WorkingCopy;
while(shouldFilter) {
int idx_start = lines.IndexOf(conflict_start_marker);
if (idx_start == -1) {
shouldFilter = false;
break;
}

int idx_sep = lines.IndexOf(conflict_separator_marker, idx_start);
if (idx_sep == -1) {
shouldFilter = false;
break;
}

int idx_end = idx_sep + 1;
while(idx_end < lines.Count - 1) {
bool found = false;
for (int i = 0; i < their_branches.Length; i++) {
string ce_marker = conflict_end_marker + their_branches[i].Name;
if(lines[idx_end].StartsWith(ce_marker)) {
found = true;
break;
}
}
if (found) {
break;
}
idx_end++;
}
if (idx_end == lines.Count - 1) {
shouldFilter = false;
break;
}

switch (DisplaySide) {
case Side.Ours:
lines.RemoveRange(idx_sep, idx_end - idx_sep + 1);
lines.RemoveAt(idx_start);
break;
case Side.WorkingCopy:
break;
case Side.Theirs:
lines.RemoveAt(idx_end);
lines.RemoveRange(idx_start, idx_sep - idx_start + 1);
break;
}
}

var builder = new StringBuilder();
foreach (var line in lines)
{
if (line.Length > 10000)
{
builder.Append(line.Substring(0, 1000));
builder.Append($"...({line.Length - 1000} characters trimmed)");
builder.AppendLine();
}
else
{
builder.AppendLine(line);
}
}

Text = builder.ToString();
}
else
{
Text = string.Empty;
}
}
}
}
141 changes: 140 additions & 1 deletion src/Views/WorkingCopy.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,146 @@
<ContentControl Content="{Binding DetailContext}">
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:Conflict">
<v:Conflict/>
<Border Background="{DynamicResource Brush.Window}" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}">
<Grid VerticalAlignment="Center">
<StackPanel Orientation="Vertical" IsVisible="{Binding !IsResolved}">
<StackPanel.DataTemplates>
<DataTemplate DataType="m:Branch">
<StackPanel Orientation="Horizontal">
<Path Width="12" Height="12" Data="{StaticResource Icons.Branch}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding FriendlyName}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding Head, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange"/>
</StackPanel>
</DataTemplate>

<DataTemplate DataType="m:Commit">
<StackPanel Orientation="Horizontal">
<Path Width="12" Height="12" Data="{StaticResource Icons.Commit}"/>
<v:CommitRefsPresenter Margin="8,0,0,0"
TagBackground="{DynamicResource Brush.DecoratorTag}"
Foreground="{DynamicResource Brush.FG1}"
FontFamily="{DynamicResource Fonts.Primary}"
FontSize="11"
VerticalAlignment="Center"
UseGraphColor="False"/>
<TextBlock Margin="4,0,0,0" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange"/>
<TextBlock Margin="4,0,0,0" Text="{Binding Subject}"/>
</StackPanel>
</DataTemplate>

<DataTemplate DataType="m:Tag">
<StackPanel Orientation="Horizontal">
<Path Width="12" Height="12" Data="{StaticResource Icons.Tag}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding Name}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange"/>
</StackPanel>
</DataTemplate>
</StackPanel.DataTemplates>

<StackPanel Orientation="Horizontal">
<Path Width="32" Height="32" Margin="10,10,10,10"
Data="{StaticResource Icons.Conflict}" Fill="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
<TextBlock Margin="10,10,10,10" FontSize="20" FontWeight="Bold"
Text="{DynamicResource Text.WorkingCopy.Conflicts}"
Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>

<Border Margin="16,0" Padding="8" CornerRadius="4" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}">
<Border.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="Theirs" Converter="{x:Static ObjectConverters.IsNotNull}"/>
<Binding Path="Mine" Converter="{x:Static ObjectConverters.IsNotNull}"/>
</MultiBinding>
</Border.IsVisible>

<Grid Margin="8,0,0,0" RowDefinitions="32,32,*" ColumnDefinitions="*,1,*,1,*,1,12">
<TextBlock Grid.Row="0" Grid.Column="0" Classes="info_label" HorizontalAlignment="Left" Text="MINE"/>
<ContentControl Grid.Row="1" Grid.Column="0" Margin="16,0,0,0" HorizontalAlignment="Left" Content="{Binding Mine}"/>

<TextBlock Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Classes="info_label" HorizontalAlignment="Center" Text="{DynamicResource Text.WorkingCopy}"/>

<TextBlock Grid.Row="0" Grid.Column="4" Classes="info_label" HorizontalAlignment="Right" Text="THEIRS"/>
<ContentControl Grid.Row="1" Grid.Column="4" Margin="16,0,0,0" HorizontalAlignment="Right" Content="{Binding Theirs}"/>

<v:ConflictTextDiffPresenter Grid.Row="2" Grid.Column="0"
x:Name="OurSidePresenter"
DisplaySide="0"
Foreground="{DynamicResource Brush.FG1}"
LineBrush="{DynamicResource Brush.Border2}"
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
IndicatorForeground="{DynamicResource Brush.FG2}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}"
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="False"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}"
EnableChunkSelection="True"/>

<Rectangle Grid.Row="2" Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>

<v:ConflictTextDiffPresenter Grid.Row="2" Grid.Column="2"
x:Name="WorkingCopyPresenter"
DisplaySide="1"
Foreground="{DynamicResource Brush.FG1}"
LineBrush="{DynamicResource Brush.Border2}"
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
IndicatorForeground="{DynamicResource Brush.FG2}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}"
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="False"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}"/>

<Rectangle Grid.Row="2" Grid.Column="3" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>

<v:ConflictTextDiffPresenter Grid.Row="2" Grid.Column="4"
x:Name="TherrSidePresenter"
DisplaySide="2"
Foreground="{DynamicResource Brush.FG1}"
LineBrush="{DynamicResource Brush.Border2}"
EmptyContentBackground="{DynamicResource Brush.Diff.EmptyBG}"
AddedContentBackground="{DynamicResource Brush.Diff.AddedBG}"
DeletedContentBackground="{DynamicResource Brush.Diff.DeletedBG}"
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
IndicatorForeground="{DynamicResource Brush.FG2}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=EditorFontSize}"
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="False"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowHiddenSymbolsInDiffView}"/>

<Rectangle Grid.Row="2" Grid.Column="5" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>

<v:TextDiffViewMinimap Grid.Column="6"
DisplayRange="{Binding #OurSidePresenter.DisplayRange}"
AddedLineBrush="{DynamicResource Brush.Diff.AddedBG}"
DeletedLineBrush="{DynamicResource Brush.Diff.DeletedBG}"/>
</Grid>
</Border>

<StackPanel Margin="0,8,0,0" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Classes="flat" Content="USE THEIRS" Command="{Binding UseTheirs}"/>
<Button Classes="flat" Margin="8,0,0,0" Content="USE MINE" Command="{Binding UseMine}"/>
<Button Classes="flat" Margin="8,0,0,0" Content="OPEN EXTERNAL MERGETOOL" Command="{Binding OpenExternalMergeTool}"/>
</StackPanel>
</StackPanel>

<StackPanel Orientation="Vertical" IsVisible="{Binding IsResolved}">
<Path Width="64" Height="64" Data="{StaticResource Icons.Check}" Fill="Green"/>
<TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts.Resolved}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
<TextBlock Text="{DynamicResource Text.WorkingCopy.CanStageTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>

<DataTemplate DataType="vm:DiffContext">
Expand Down