feature: supports to drop HEAD commit (reset to its first parent) (#1832)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-09-29 11:38:37 +08:00
parent 93edb8ec0a
commit 77b0fde603
8 changed files with 147 additions and 1 deletions

View File

@@ -135,6 +135,7 @@
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.CommitCM.CopySubject" xml:space="preserve">Subject</x:String>
<x:String x:Key="Text.CommitCM.CustomAction" xml:space="preserve">Custom Action</x:String>
<x:String x:Key="Text.CommitCM.Drop" xml:space="preserve">Drop Commit</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">Interactive Rebase</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Drop" xml:space="preserve">Drop...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Edit" xml:space="preserve">Edit...</x:String>
@@ -355,6 +356,9 @@
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">Include untracked files</x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">{0} changes will be discarded</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">You can't undo this action!!!</x:String>
<x:String x:Key="Text.DropHead" xml:space="preserve">Drop Commit</x:String>
<x:String x:Key="Text.DropHead.Commit" xml:space="preserve">Commit:</x:String>
<x:String x:Key="Text.DropHead.NewHead" xml:space="preserve">New HEAD:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">Bookmark:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Name" xml:space="preserve">New Name:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Target" xml:space="preserve">Target:</x:String>

View File

@@ -139,6 +139,7 @@
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">提交指纹</x:String>
<x:String x:Key="Text.CommitCM.CopySubject" xml:space="preserve">主题</x:String>
<x:String x:Key="Text.CommitCM.CustomAction" xml:space="preserve">自定义操作</x:String>
<x:String x:Key="Text.CommitCM.Drop" xml:space="preserve">丢弃此提交</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">交互式变基(rebase -i)</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Drop" xml:space="preserve">丢弃...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Edit" xml:space="preserve">编辑...</x:String>
@@ -359,6 +360,9 @@
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">包括未跟踪的文件</x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">总计{0}项选中更改</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">本操作不支持回退,请确认后继续!!!</x:String>
<x:String x:Key="Text.DropHead" xml:space="preserve">丢弃提交</x:String>
<x:String x:Key="Text.DropHead.Commit" xml:space="preserve">提交 </x:String>
<x:String x:Key="Text.DropHead.NewHead" xml:space="preserve">丢弃后 HEAD </x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">书签 </x:String>
<x:String x:Key="Text.EditRepositoryNode.Name" xml:space="preserve">名称 </x:String>
<x:String x:Key="Text.EditRepositoryNode.Target" xml:space="preserve">目标 </x:String>

View File

@@ -139,6 +139,7 @@
<x:String x:Key="Text.CommitCM.CopySHA" xml:space="preserve">提交編號</x:String>
<x:String x:Key="Text.CommitCM.CopySubject" xml:space="preserve">標題</x:String>
<x:String x:Key="Text.CommitCM.CustomAction" xml:space="preserve">自訂動作</x:String>
<x:String x:Key="Text.CommitCM.Drop" xml:space="preserve">捨棄此提交</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase" xml:space="preserve">互動式重定基底 (rebase -i)</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Drop" xml:space="preserve">捨棄...</x:String>
<x:String x:Key="Text.CommitCM.InteractiveRebase.Edit" xml:space="preserve">編輯...</x:String>
@@ -359,6 +360,9 @@
<x:String x:Key="Text.Discard.IncludeUntracked" xml:space="preserve">包含未追蹤檔案</x:String>
<x:String x:Key="Text.Discard.Total" xml:space="preserve">將捨棄總計 {0} 項已選取的變更</x:String>
<x:String x:Key="Text.Discard.Warning" xml:space="preserve">您無法復原此操作,請確認後再繼續!</x:String>
<x:String x:Key="Text.DropHead" xml:space="preserve">捨棄提交</x:String>
<x:String x:Key="Text.DropHead.Commit" xml:space="preserve">提交 </x:String>
<x:String x:Key="Text.DropHead.NewHead" xml:space="preserve">捨棄後新的 HEAD </x:String>
<x:String x:Key="Text.EditRepositoryNode.Bookmark" xml:space="preserve">書籤:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Name" xml:space="preserve">名稱:</x:String>
<x:String x:Key="Text.EditRepositoryNode.Target" xml:space="preserve">目標:</x:String>

View File

@@ -0,0 +1,63 @@
using System.Threading.Tasks;
namespace SourceGit.ViewModels
{
public class DropHead : Popup
{
public Models.Commit Target
{
get;
}
public Models.Commit NewHead
{
get;
}
public DropHead(Repository repo, Models.Commit target, Models.Commit parent)
{
_repo = repo;
Target = target;
NewHead = parent;
}
public override async Task<bool> Sure()
{
using var lockWatcher = _repo.LockWatcher();
ProgressDescription = $"Drop HEAD '{Target.SHA}' ...";
var log = _repo.CreateLog($"Drop '{Target.SHA}'");
Use(log);
var changes = await new Commands.QueryLocalChanges(_repo.FullPath, true).GetResultAsync();
var needAutoStash = changes.Count > 0;
var succ = false;
if (needAutoStash)
{
succ = await new Commands.Stash(_repo.FullPath)
.Use(log)
.PushAsync("DROP_HEAD_AUTO_STASH", true);
if (!succ)
{
log.Complete();
return false;
}
}
succ = await new Commands.Reset(_repo.FullPath, NewHead.SHA, "--hard")
.Use(log)
.ExecAsync();
if (succ && needAutoStash)
await new Commands.Stash(_repo.FullPath)
.Use(log)
.PopAsync("stash@{0}");
log.Complete();
return succ;
}
private readonly Repository _repo;
}
}

View File

@@ -332,6 +332,16 @@ namespace SourceGit.ViewModels
}
}
public async Task DropHeadAsync(Models.Commit head)
{
var parent = _commits.Find(x => x.SHA.Equals(head.Parents[0]));
if (parent == null)
parent = await new Commands.QuerySingleCommit(_repo.FullPath, head.Parents[0]).GetResultAsync();
if (parent != null && _repo.CanCreatePopup())
_repo.ShowPopup(new DropHead(_repo, head, parent));
}
public async Task InteractiveRebaseAsync(Models.Commit commit, Models.InteractiveRebaseAction act)
{
var prefill = new InteractiveRebasePrefill(commit.SHA, act);

37
src/Views/DropHead.axaml Normal file
View File

@@ -0,0 +1,37 @@
<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:m="using:SourceGit.Models"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.DropHead"
x:DataType="vm:DropHead">
<StackPanel Orientation="Vertical" Margin="8,0">
<TextBlock FontSize="18"
Classes="bold"
Text="{DynamicResource Text.DropHead}"/>
<Grid Margin="0,16,0,0" RowDefinitions="32,24" ColumnDefinitions="140,*">
<TextBlock Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.DropHead.Commit}"/>
<Grid Grid.Row="0" Grid.Column="1" ColumnDefinitions="Auto,Auto,*" Height="20" VerticalAlignment="Center">
<Path Grid.Column="0" Margin="0,0,8,0" Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Commit}"/>
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding Target.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange"/>
<TextBlock Grid.Column="2" Text="{Binding Target.Subject}" Margin="8,0,0,0" TextTrimming="CharacterEllipsis"/>
</Grid>
<TextBlock Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Right" VerticalAlignment="Center"
Margin="0,0,8,0"
Text="{DynamicResource Text.DropHead.NewHead}"/>
<Grid Grid.Row="1" Grid.Column="1" ColumnDefinitions="Auto,Auto,*" Height="20" VerticalAlignment="Center">
<Path Grid.Column="0" Margin="0,0,8,0" Width="14" Height="14" Fill="{DynamicResource Brush.FG1}" Data="{StaticResource Icons.Commit}"/>
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding NewHead.SHA, Converter={x:Static c:StringConverters.ToShortSHA}}" Foreground="DarkOrange"/>
<TextBlock Grid.Column="2" Text="{Binding NewHead.Subject}" Margin="8,0,0,0" TextTrimming="CharacterEllipsis"/>
</Grid>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace SourceGit.Views
{
public partial class DropHead : UserControl
{
public DropHead()
{
InitializeComponent();
}
}
}

View File

@@ -610,7 +610,19 @@ namespace SourceGit.Views
};
menu.Items.Add(revert);
if (!isHead)
if (isHead)
{
var dropHead = new MenuItem();
dropHead.Header = App.Text("CommitCM.Drop");
dropHead.Icon = App.CreateMenuIcon("Icons.Clear");
dropHead.Click += async (_, e) =>
{
await vm.DropHeadAsync(commit);
e.Handled = true;
};
menu.Items.Add(dropHead);
}
else
{
var checkoutCommit = new MenuItem();
checkoutCommit.Header = App.Text("CommitCM.Checkout");