diff --git a/src/Commands/Reword.cs b/src/Commands/Reword.cs new file mode 100644 index 000000000..7dd03f940 --- /dev/null +++ b/src/Commands/Reword.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.Commands +{ + public class Reword : Command + { + public Reword(string repo, string message) + { + _tmpFile = Path.GetTempFileName(); + _message = message; + + WorkingDirectory = repo; + Context = repo; + Args = $"commit --amend --only --allow-empty --file={_tmpFile.Quoted()}"; + } + + public async Task RunAsync() + { + try + { + await File.WriteAllTextAsync(_tmpFile, _message).ConfigureAwait(false); + var succ = await ExecAsync().ConfigureAwait(false); + File.Delete(_tmpFile); + return succ; + } + catch + { + return false; + } + } + + private readonly string _tmpFile; + private readonly string _message; + } +} diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index eb8ff1ca7..762fc9992 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -171,6 +171,7 @@ Squash into Parent... Merge... Push ${0}$ to ${1}$... + Quick Reword... Rebase ${0}$ on ${1}$... Reset ${0}$ to ${1}$... Revert Commit... @@ -877,6 +878,9 @@ Revert Commit Commit: Commit revert changes + Reword Commit + Commit: + Message: Running. Please wait... SAVE Save As... diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 761cc724a..cb91e13fa 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -175,6 +175,7 @@ 合并至父提交... 合并(merge)... 推送(push) ${0}$ 到 ${1}$... + 快速修改提交信息... 变基(rebase) ${0}$ 到 ${1}$... 重置(reset) ${0}$ 到 ${1}$... 回滚此提交... @@ -881,6 +882,9 @@ 回滚操作确认 目标提交 : 回滚后提交更改 + 修改提交信息 + 目标提交 : + 提交信息: 执行操作中,请耐心等待... 保 存 另存为... diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 135596f6e..b3ca3f2cd 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -175,6 +175,7 @@ 合併至父提交... 合併 (merge)... 推送 (push) ${0}$ 至 ${1}$... + 快速修改提交訊息... 重定基底 (rebase) ${0}$ 至 ${1}$... 重設 (reset) ${0}$ 至 ${1}$... 復原此提交... @@ -880,6 +881,9 @@ 復原操作確認 目標提交: 復原後提交變更 + 修改提交訊息 + 目標提交: + 提交訊息: 正在執行操作,請耐心等待... 儲存 另存新檔... diff --git a/src/ViewModels/Reword.cs b/src/ViewModels/Reword.cs new file mode 100644 index 000000000..eeab178aa --- /dev/null +++ b/src/ViewModels/Reword.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; + +namespace SourceGit.ViewModels +{ + public class Reword : Popup + { + public Models.Commit Target + { + get; + } + + public string Message + { + get; + } + + public Reword(Repository repo, Models.Commit target, string message) + { + _repo = repo; + Target = target; + Message = message; + } + + public override async Task Sure() + { + using var lockWatcher = _repo.LockWatcher(); + ProgressDescription = $"Reword commit '{Target.SHA}' ..."; + + var log = _repo.CreateLog($"Reword '{Target.SHA}'"); + Use(log); + + var succ = Target.IsCurrentHead + ? await RewordHeadAsync(log) + : await RewordWithRebaseAsync(log); + + log.Complete(); + + if (succ) + { + // The rewritten commit sits at the same distance from HEAD as the original did, + // so re-select it once the history is reloaded instead of jumping back to the top. + var sha = await new Commands.QueryRevisionByRefName(_repo.FullPath, $"HEAD~{_distance}").GetResultAsync(); + if (!string.IsNullOrEmpty(sha)) + _repo.NavigateToCommit(sha, true); + } + + _repo.MarkBranchesDirtyManually(); + return true; + } + + private async Task RewordHeadAsync(CommandLog log) + { + _distance = 0; + return await new Commands.Reword(_repo.FullPath, Message) + .Use(log) + .RunAsync(); + } + + private async Task RewordWithRebaseAsync(CommandLog log) + { + var onto = Target.Parents[0]; + var commits = await new Commands.QueryCommitsForInteractiveRebase(_repo.FullPath, onto).GetResultAsync(); + + var collection = new Models.InteractiveRebaseJobCollection(); + collection.OrigHead = _repo.CurrentBranch.Head; + collection.Onto = onto; + + _distance = -1; + for (var i = commits.Count - 1; i >= 0; i--) + { + var c = commits[i]; + var isTarget = c.Commit.SHA.Equals(Target.SHA, StringComparison.Ordinal); + if (isTarget) + _distance = i; + + collection.Jobs.Add(new Models.InteractiveRebaseJob() + { + SHA = c.Commit.SHA, + Action = isTarget ? Models.InteractiveRebaseAction.Reword : Models.InteractiveRebaseAction.Pick, + Message = isTarget ? Message : c.Message, + }); + } + + if (_distance < 0) + { + _repo.SendNotification($"Commit '{Target.SHA}' is not reachable from HEAD!", true); + return false; + } + + var jobsFile = Path.Combine(_repo.GitDir, "sourcegit.interactive_rebase"); + await using (var stream = File.Create(jobsFile)) + { + await JsonSerializer.SerializeAsync(stream, collection, JsonCodeGen.Default.InteractiveRebaseJobCollection); + } + + return await new Commands.InteractiveRebase(_repo.FullPath, onto, true, false) + .Use(log) + .ExecAsync(); + } + + private readonly Repository _repo = null; + private int _distance = 0; + } +} diff --git a/src/Views/CommitMessageEditor.axaml b/src/Views/CommitMessageEditor.axaml index ee1aac3c8..435c4dd30 100644 --- a/src/Views/CommitMessageEditor.axaml +++ b/src/Views/CommitMessageEditor.axaml @@ -8,11 +8,10 @@ x:Name="ThisControl" Icon="/App.ico" Title="{DynamicResource Text.CodeEditor}" - Width="800" - SizeToContent="Height" - CanResize="False" + Width="800" Height="520" + MinWidth="600" MinHeight="360" WindowStartupLocation="CenterScreen"> - + - - + - -