Skip to content

Commit 3d07a89

Browse files
committed
Fix reset file commands for deleted files in compare view
When resetting a file to a revision where it is deleted, use 'git rm' instead of 'git checkout' to properly remove the file from both the working directory and the index. - Add new Remove command for git rm operations - Update ResetToSourceRevisionAsync to handle Added files (don't exist in source) - Update ResetToTargetRevisionAsync to handle Deleted files (don't exist in target) - Update multiple file reset methods to separate files into checkout vs remove lists - Pass Change object instead of just path string to enable state checking - Use --force and --ignore-unmatch flags for robust file removal Fixes the "pathspec did not match any file(s) known to git" error when trying to reset deleted files to the revision where they are deleted.
1 parent e25e57c commit 3d07a89

3 files changed

Lines changed: 93 additions & 12 deletions

File tree

src/Commands/Remove.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace SourceGit.Commands
2+
{
3+
public class Remove : Command
4+
{
5+
public Remove(string repo)
6+
{
7+
WorkingDirectory = repo;
8+
Context = repo;
9+
}
10+
11+
public Remove File(string file)
12+
{
13+
Args = $"rm --force --ignore-unmatch -- {file.Quoted()}";
14+
return this;
15+
}
16+
17+
public Remove Files(string pathspecFromFile)
18+
{
19+
Args = $"rm --force --ignore-unmatch --pathspec-from-file={pathspecFromFile.Quoted()}";
20+
return this;
21+
}
22+
}
23+
}

src/ViewModels/RevisionCompare.cs

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,25 +149,45 @@ public async Task SaveChangesAsPatchAsync(List<Models.Change> changes, string sa
149149
App.SendNotification(_repo, App.Text("SaveAsPatchSuccess"));
150150
}
151151

152-
public async Task ResetToSourceRevisionAsync(string path)
152+
public async Task ResetToSourceRevisionAsync(Models.Change change)
153153
{
154154
var sourceSHA = GetSHA(_startPoint);
155155
if (string.IsNullOrEmpty(sourceSHA))
156156
return;
157157

158158
var log = _repository?.CreateLog($"Reset File to '{sourceSHA}'");
159-
await new Commands.Checkout(_repo).Use(log).FileWithRevisionAsync(path, sourceSHA);
159+
160+
// If file is Added in diff, it doesn't exist in source - remove it
161+
if (change.Index == Models.ChangeState.Added)
162+
{
163+
await new Commands.Remove(_repo).Use(log).File(change.Path).ExecAsync();
164+
}
165+
else
166+
{
167+
await new Commands.Checkout(_repo).Use(log).FileWithRevisionAsync(change.Path, sourceSHA);
168+
}
169+
160170
log?.Complete();
161171
}
162172

163-
public async Task ResetToTargetRevisionAsync(string path)
173+
public async Task ResetToTargetRevisionAsync(Models.Change change)
164174
{
165175
var targetSHA = GetSHA(_endPoint);
166176
if (string.IsNullOrEmpty(targetSHA))
167177
return;
168178

169179
var log = _repository?.CreateLog($"Reset File to '{targetSHA}'");
170-
await new Commands.Checkout(_repo).Use(log).FileWithRevisionAsync(path, targetSHA);
180+
181+
// If file is Deleted in diff, it doesn't exist in target - remove it
182+
if (change.Index == Models.ChangeState.Deleted)
183+
{
184+
await new Commands.Remove(_repo).Use(log).File(change.Path).ExecAsync();
185+
}
186+
else
187+
{
188+
await new Commands.Checkout(_repo).Use(log).FileWithRevisionAsync(change.Path, targetSHA);
189+
}
190+
171191
log?.Complete();
172192
}
173193

@@ -177,12 +197,31 @@ public async Task ResetMultipleToSourceRevisionAsync(List<Models.Change> changes
177197
if (string.IsNullOrEmpty(sourceSHA))
178198
return;
179199

180-
var files = new List<string>();
200+
var filesToCheckout = new List<string>();
201+
var filesToRemove = new List<string>();
202+
203+
// Separate files: Added files don't exist in source, so remove them
181204
foreach (var c in changes)
182-
files.Add(c.Path);
205+
{
206+
if (c.Index == Models.ChangeState.Added)
207+
filesToRemove.Add(c.Path);
208+
else
209+
filesToCheckout.Add(c.Path);
210+
}
183211

184212
var log = _repository?.CreateLog($"Reset Files to '{sourceSHA}'");
185-
await new Commands.Checkout(_repo).Use(log).MultipleFilesWithRevisionAsync(files, sourceSHA);
213+
214+
if (filesToCheckout.Count > 0)
215+
await new Commands.Checkout(_repo).Use(log).MultipleFilesWithRevisionAsync(filesToCheckout, sourceSHA);
216+
217+
if (filesToRemove.Count > 0)
218+
{
219+
var pathSpecFile = System.IO.Path.GetTempFileName();
220+
await System.IO.File.WriteAllLinesAsync(pathSpecFile, filesToRemove);
221+
await new Commands.Remove(_repo).Use(log).Files(pathSpecFile).ExecAsync();
222+
System.IO.File.Delete(pathSpecFile);
223+
}
224+
186225
log?.Complete();
187226
}
188227

@@ -192,12 +231,31 @@ public async Task ResetMultipleToTargetRevisionAsync(List<Models.Change> changes
192231
if (string.IsNullOrEmpty(targetSHA))
193232
return;
194233

195-
var files = new List<string>();
234+
var filesToCheckout = new List<string>();
235+
var filesToRemove = new List<string>();
236+
237+
// Separate files: Deleted files don't exist in target, so remove them
196238
foreach (var c in changes)
197-
files.Add(c.Path);
239+
{
240+
if (c.Index == Models.ChangeState.Deleted)
241+
filesToRemove.Add(c.Path);
242+
else
243+
filesToCheckout.Add(c.Path);
244+
}
198245

199246
var log = _repository?.CreateLog($"Reset Files to '{targetSHA}'");
200-
await new Commands.Checkout(_repo).Use(log).MultipleFilesWithRevisionAsync(files, targetSHA);
247+
248+
if (filesToCheckout.Count > 0)
249+
await new Commands.Checkout(_repo).Use(log).MultipleFilesWithRevisionAsync(filesToCheckout, targetSHA);
250+
251+
if (filesToRemove.Count > 0)
252+
{
253+
var pathSpecFile = System.IO.Path.GetTempFileName();
254+
await System.IO.File.WriteAllLinesAsync(pathSpecFile, filesToRemove);
255+
await new Commands.Remove(_repo).Use(log).Files(pathSpecFile).ExecAsync();
256+
System.IO.File.Delete(pathSpecFile);
257+
}
258+
201259
log?.Complete();
202260
}
203261

src/Views/RevisionCompare.axaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
114114
resetToSource.Icon = App.CreateMenuIcon("Icons.File.Checkout");
115115
resetToSource.Click += async (_, ev) =>
116116
{
117-
await vm.ResetToSourceRevisionAsync(change.Path);
117+
await vm.ResetToSourceRevisionAsync(change);
118118
ev.Handled = true;
119119
};
120120

@@ -123,7 +123,7 @@ private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e
123123
resetToTarget.Icon = App.CreateMenuIcon("Icons.File.Checkout");
124124
resetToTarget.Click += async (_, ev) =>
125125
{
126-
await vm.ResetToTargetRevisionAsync(change.Path);
126+
await vm.ResetToTargetRevisionAsync(change);
127127
ev.Handled = true;
128128
};
129129

0 commit comments

Comments
 (0)