8000 Merge pull request #907 from libgit2/ntk/index_lowlevel · GiTechLab/libgit2sharp@5f7460d · GitHub
[go: up one dir, main page]

Skip to content

Commit 5f7460d

Browse files
committed
Merge pull request libgit2#907 from libgit2/ntk/index_lowlevel
Expose more low level Index operations
2 parents 05e81e7 + fed306b commit 5f7460d

File tree

3 files changed

+205
-20
lines changed

3 files changed

+205
-20
lines changed

LibGit2Sharp.Tests/IndexFixture.cs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,5 +373,109 @@ public void CanClearTheIndex()
373373
Assert.Equal(FileStatus.Removed | FileStatus.Untracked, repo.RetrieveStatus(testFile));
374374
}
375375
}
376+
377+
[Theory]
378+
[InlineData("new_tracked_file.txt", FileStatus.Added, FileStatus.Untracked)]
379+
[InlineData("modified_staged_file.txt", FileStatus.Staged, FileStatus.Removed | FileStatus.Untracked)]
380+
[InlineData("i_dont_exist.txt", FileStatus.Nonexistent, FileStatus.Nonexistent)]
381+
public void CanRemoveAnEntryFromTheIndex(string pathInTheIndex, FileStatus expectedBeforeStatus, FileStatus expectedAfterStatus)
382+
{
383+
var path = SandboxStandardTestRepoGitDir();
384+
using (var repo = new Repository(path))
385+
{
386+
var before = repo.RetrieveStatus(pathInTheIndex);
387+
Assert.Equal(expectedBeforeStatus, before);
388+
389+
repo.Index.Remove(pathInTheIndex);
390+
391+
var after = repo.RetrieveStatus(pathInTheIndex);
392+
Assert.Equal(expectedAfterStatus, after);
393+
}
394+
}
395+
396+
[Theory]
397+
[InlineData("new_untracked_file.txt", FileStatus.Untracked, FileStatus.Added)]
398+
[InlineData("modified_unstaged_file.txt", FileStatus.Modified, FileStatus.Staged)]
399+
public void CanAddAnEntryToTheIndexFromAFileInTheWorkdir(string pathInTheWorkdir, FileStatus expectedBeforeStatus, FileStatus expectedAfterStatus)
400+
{
401+
var path = SandboxStandardTestRepoGitDir();
402+
using (var repo = new Repository(path))
403+
{
404+
var before = repo.RetrieveStatus(pathInTheWorkdir);
405+
Assert.Equal(expectedBeforeStatus, before);
406+
407+
repo.Index.Add(pathInTheWorkdir);
408+
409+
var after = repo.RetrieveStatus(pathInTheWorkdir);
410+
Assert.Equal(expectedAfterStatus, after);
411+
}
412+
}
413+
414+
[Fact]
415+
public void CanAddAnEntryToTheIndexFromABlob()
416+
{
417+
var path = SandboxStandardTestRepoGitDir();
418+
using (var repo = new Repository(path))
419+
{
420+
const string targetIndexEntryPath = "1.txt";
421+
var before = repo.RetrieveStatus(targetIndexEntryPath);
422+
Assert.Equal(FileStatus.Unaltered, before);
423+
424+
var blob = repo.Lookup<Blob>("a8233120f6ad708f843d861ce2b7228ec4e3dec6");
425+
426+
repo.Index.Add(blob, targetIndexEntryPath, Mode.NonExecutableFile);
427+
428+
var after = repo.RetrieveStatus(targetIndexEntryPath);
429+
Assert.Equal(FileStatus.Staged | FileStatus.Modified, after);
430+
}
431+
}
432+
433+
[Fact]
434+
public void AddingAnEntryToTheIndexFromAUnknwonFileInTheWorkdirThrows()
435+
{
436+
var path = SandboxStandardTestRepoGitDir();
437+
using (var repo = new Repository(path))
438+
{
439+
const string filePath = "i_dont_exist.txt";
440+
var before = repo.RetrieveStatus(filePath);
441+
Assert.Equal(FileStatus.Nonexistent, before);
442+
443+
Assert.Throws<NotFoundException>(() => repo.Index.Add(filePath));
444+
}
445+
}
446+
447+
[Fact]
448+
public void CanMimicGitAddAll()
449+
{
450+
var path = SandboxStandardTestRepoGitDir();
451+
using (var repo = new Repository(path))
452+
{
453+
var before = repo.RetrieveStatus();
454+
Assert.True(before.Any(se => se.State == FileStatus.Untracked));
455+
Assert.True(before.Any(se => se.State == FileStatus.Modified));
456+
Assert.True(before.Any(se => se.State == FileStatus.Missing));
457+
458+
AddSomeCornerCases(repo);
459+
460+
repo.Stage("*");
461+
462+
var after = repo.RetrieveStatus();
463+
Assert.False(after.Any(se => se.State == FileStatus.Untracked));
464+
Assert.False(after.Any(se => se.State == FileStatus.Modified));
465+
Assert.False(after.Any(se => se.State == FileStatus.Missing));
466+
}
467+
}
468+
469+
private static void AddSomeCornerCases(Repository repo)
470+
{
471+
// Turn 1.txt into a directory in the Index
472+
repo.Index.Remove("1.txt");
473+
var blob = repo.Lookup<Blob>("a8233120f6ad708f843d861ce2b7228ec4e3dec6");
474+
repo.Index.Add(blob, "1.txt/Sneaky", Mode.NonExecutableFile);
475+
476+
// Turn README into a symlink
477+
Blob linkContent = OdbHelper.CreateBlob(repo, "1.txt/sneaky");
478+
repo.Index.Add(linkContent, "README", Mode.SymbolicLink);
479+
}
376480
}
377481
}

LibGit2Sharp/Index.cs

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,75 @@ public virtual void Clear()
157157
UpdatePhysicalIndex();
158158
}
159159

160-
private string RemoveFromIndex(string relativePath)
160+
private void RemoveFromIndex(string relativePath)
161161
{
162162
Proxy.git_index_remove_bypath(handle, relativePath);
163+
}
164+
165+
/// <summary>
166+
/// Removes a specified entry from the index.
167+
/// </summary>
168+
/// <param name="indexEntryPath">The path of the <see cref="Index"/> entry to be removed.</param>
169+
public virtual void Remove(string indexEntryPath)
170+
{
171+
if (indexEntryPath == null)
172+
{
173+
throw new ArgumentNullException("indexEntryPath");
174+
}
175+
176+
RemoveFromIndex(indexEntryPath);
177+
178+
UpdatePhysicalIndex();
179+
}
180+
181+
/// <summary>
182+
/// Adds a file from the workdir in the <see cref="Index"/>.
183+
/// <para>
184+
/// If an entry with the same path already exists in the <see cref="Index"/>,
185+
/// the newly added one will overwrite it.
186+
/// </para>
187+
/// </summary>
188+
/// <param name="pathInTheWorkdir">The path, in the working directory, of the file to be added.</param>
189+
public virtual void Add(string pathInTheWorkdir)
190+
{
191+
if (pathInTheWorkdir == null)
192+
{
193+
throw new ArgumentNullException("pathInTheWorkdir");
194+
}
163195

164-
return relativePath;
196+
Proxy.git_index_add_bypath(handle, pathInTheWorkdir);
197+
198+
UpdatePhysicalIndex();
199+
}
200+
201+
/// <summary>
202+
/// Adds an entry in the <see cref="Index"/> from a <see cref="Blob"/>.
203+
/// <para>
204+
/// If an entry with the same path already exists in the <see cref="Index"/>,
205+
/// the newly added one will overwrite it.
206+
/// </para>
207+
/// </summary>
208+
/// <param name="blob">The <see cref="Blob"/> which content should be added to the <see cref="Index"/>.</param>
209+
/// <param name="indexEntryPath">The path to be used in the <see cref="Index"/>.</param>
210+
/// <param name="indexEntryMode">Either <see cref="Mode.NonExecutableFile"/>, <see cref="Mode.ExecutableFile"/>
211+
/// or <see cref="Mode.SymbolicLink"/>.</param>
212+
public virtual void Add(Blob blob, string indexEntryPath, Mode indexEntryMode)
213+
{
214+
Ensure.ArgumentConformsTo(indexEntryMode, m => m.HasAny(TreeEntryDefinition.BlobModes), "indexEntryMode");
215+
216+
if (blob == null)
217+
{
218+
throw new ArgumentNullException("blob");
219+
}
220+
221+
if (indexEntryPath == null)
222+
{
223+
throw new ArgumentNullException("indexEntryPath");
224+
}
225+
226+
AddEntryToTheIndex(indexEntryPath, blob.Id, indexEntryMode);
227+
228+
UpdatePhysicalIndex();
165229
}
166230

167231
private void UpdatePhysicalIndex()
@@ -185,7 +249,11 @@ internal void Replace(TreeChanges changes)
185249
case ChangeKind.Deleted:
186250
/* Fall through */
187251
case ChangeKind.Modified:
188-
ReplaceIndexEntryWith(treeEntryChanges);
252+
AddEntryToTheIndex(
253+
treeEntryChanges.OldPath,
254+
treeEntryChanges.OldOid,
255+
treeEntryChanges.OldMode);
256+
189257
continue;
190258

191259
default:
@@ -207,13 +275,13 @@ public virtual ConflictCollection Conflicts
207275
}
208276
}
209277

210-
private void ReplaceIndexEntryWith(TreeEntryChanges treeEntryChanges)
278+
private void AddEntryToTheIndex(string path, ObjectId id, Mode mode)
211279
{
212280
var indexEntry = new GitIndexEntry
213281
{
214-
Mode = (uint)treeEntryChanges.OldMode,
215-
Id = treeEntryChanges.OldOid.Oid,
216-
Path = StrictFilePathMarshaler.FromManaged(treeEntryChanges.OldPath),
282+
Mode = (uint)mode,
283+
Id = id.Oid,
284+
Path = StrictFilePathMarshaler.FromManaged(path),
217285
};
218286

219287
Proxy.git_index_add(handle, indexEntry);

LibGit2Sharp/Repository.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,28 +1493,41 @@ public void Stage(IEnumerable<string> paths, StageOptions stageOptions)
14931493
diffModifiers |= DiffModifiers.IncludeIgnored;
14941494
}
14951495

1496-
var changes = Diff.Compare<TreeChanges>(diffModifiers, paths, explicitPathsOptions);
1496+
var changes = Diff.Compare<TreeChanges>(diffModifiers, paths, explicitPathsOptions,
1497+
new CompareOptions { Similarity = SimilarityOptions.None });
14971498

1498-
foreach (var treeEntryChanges in changes)
1499+
var unexpectedTypesOfChanges = changes
1500+
.Where(
1501+
tec => tec.Status != ChangeKind.Added &&
1502+
tec.Status != ChangeKind.Modified &&
1503+
tec.Status != ChangeKind.Unmodified &&
1504+
tec.Status != ChangeKind.Deleted).ToList();
1505+
1506+
if (unexpectedTypesOfChanges.Count > 0)
14991507
{
1500-
switch (treeEntryChanges.Status)
1501-
{
1502-
case ChangeKind.Unmodified:
1503-
continue;
1508+
throw new InvalidOperationException(
1509+
string.Format(CultureInfo.InvariantCulture,
1510+
"Entry '{0}' bears an unexpected ChangeKind '{1}'",
1511+
unexpectedTypesOfChanges[0].Path, unexpectedTypesOfChanges[0].Status));
1512+
}
15041513

1505-
case ChangeKind.Deleted:
1506-
RemoveFromIndex(treeEntryChanges.Path);
1507-
continue;
1514+
foreach (TreeEntryChanges treeEntryChanges in changes
1515+
.Where(tec => tec.Status == ChangeKind.Deleted))
1516+
{
1517+
RemoveFromIndex(treeEntryChanges.Path);
1518+
}
15081519

1520+
foreach (TreeEntryChanges treeEntryChanges in changes)
1521+
{
1522+
switch (treeEntryChanges.Status)
1523+
{
15091524
case ChangeKind.Added:
1510-
/* Fall through */
15111525
case ChangeKind.Modified:
15121526
AddToIndex(treeEntryChanges.Path);
1513-
continue;
1527+
break;
15141528

15151529
default:
1516-
throw new InvalidOperationException(
1517-
string.Format(CultureInfo.InvariantCulture, "Entry '{0}' bears an unexpected ChangeKind '{1}'", treeEntryChanges.Path, treeEntryChanges.Status));
1530+
continue;
15181531
}
15191532
}
15201533

0 commit comments

Comments
 (0)
0