Skip to content

Commit 49346f8

Browse files
committed
Merge pull request #1062 from libgit2/ethomson/stage_ignores
Ensure that conflicts are staged and never ignored
2 parents 1359cc5 + 49f9530 commit 49346f8

File tree

8 files changed

+109
-3
lines changed

8 files changed

+109
-3
lines changed

LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
symlinks = false
77
ignorecase = true
88
hideDotFiles = dotGitOnly
9+
autocrlf = false

LibGit2Sharp.Tests/StageFixture.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,5 +334,43 @@ public void CanStageIgnoredPaths(string path)
334334
Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(path));
335335
}
336336
}
337+
338+
[Theory]
339+
[InlineData("new_untracked_file.txt", FileStatus.Ignored)]
340+
[InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInIndex)]
341+
public void IgnoredFilesAreOnlyStagedIfTheyreInTheRepo(string filename, FileStatus expected)
342+
{
343+
var path = SandboxStandardTestRepoGitDir();
344+
using (var repo = new Repository(path))
345+
{
346+
File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"),
347+
String.Format("{0}\n", filename));
348+
349+
repo.Stage(filename);
350+
Assert.Equal(expected, repo.RetrieveStatus(filename));
351+
}
352+
}
353+
354+
[Theory]
355+
[InlineData("ancestor-and-ours.txt", FileStatus.Unaltered)]
356+
[InlineData("ancestor-and-theirs.txt", FileStatus.NewInIndex)]
357+
[InlineData("ancestor-only.txt", FileStatus.Nonexistent)]
358+
[InlineData("conflicts-one.txt", FileStatus.ModifiedInIndex)]
359+
[InlineData("conflicts-two.txt", FileStatus.ModifiedInIndex)]
360+
[InlineData("ours-only.txt", FileStatus.Unaltered)]
361+
[InlineData("ours-and-theirs.txt", FileStatus.ModifiedInIndex)]
362+
[InlineData("theirs-only.txt", FileStatus.NewInIndex)]
363+
public void CanStageConflictedIgnoredFiles(string filename, FileStatus expected)
364+
{
365+
var path = SandboxMergedTestRepo();
366+
using (var repo = new Repository(path))
367+
{
368+
File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"),
369+
String.Format("{0}\n", filename));
370+
371+
repo.Stage(filename);
372+
Assert.Equal(expected, repo.RetrieveStatus(filename));
373+
}
374+
}
337375
}
338376
}

LibGit2Sharp/ChangeKind.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,10 @@ public enum ChangeKind
5555
/// Entry is unreadable.
5656
/// </summary>
5757
Unreadable = 9,
58+
59+
/// <summary>
60+
/// Entry is currently in conflict.
61+
/// </summary>
62+
Conflicted = 10,
5863
}
5964
}

LibGit2Sharp/Core/GitDiff.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ internal enum GitDiffFlags
231231
GIT_DIFF_FLAG_BINARY = (1 << 0),
232232
GIT_DIFF_FLAG_NOT_BINARY = (1 << 1),
233233
GIT_DIFF_FLAG_VALID_ID = (1 << 2),
234+
GIT_DIFF_FLAG_EXISTS = (1 << 3),
234235
}
235236

236237
[StructLayout(LayoutKind.Sequential)]

LibGit2Sharp/FileStatus.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,10 @@ public enum FileStatus
125125
/// The file is <see cref="NewInWorkdir"/> but its name and/or path matches an exclude pattern in a <c>gitignore</c> file.
126126
/// </summary>
127127
Ignored = (1 << 14), /* GIT_STATUS_IGNORED */
128+
129+
/// <summary>
130+
/// The file is <see cref="Conflicted"/> due to a merge.
131+
/// </summary>
132+
Conflicted = (1 << 15), /* GIT_STATUS_CONFLICTED */
128133
}
129134
}

LibGit2Sharp/Repository.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,7 @@ public void Stage(IEnumerable<string> paths, StageOptions stageOptions)
17051705
.Where(
17061706
tec => tec.Status != ChangeKind.Added &&
17071707
tec.Status != ChangeKind.Modified &&
1708+
tec.Status != ChangeKind.Conflicted &&
17081709
tec.Status != ChangeKind.Unmodified &&
17091710
tec.Status != ChangeKind.Deleted).ToList();
17101711

@@ -1716,10 +1717,25 @@ public void Stage(IEnumerable<string> paths, StageOptions stageOptions)
17161717
unexpectedTypesOfChanges[0].Path, unexpectedTypesOfChanges[0].Status));
17171718
}
17181719

1719-
foreach (TreeEntryChanges treeEntryChanges in changes
1720-
.Where(tec => tec.Status == ChangeKind.Deleted))
1720+
/* Remove files from the index that don't exist on disk */
1721+
foreach (TreeEntryChanges treeEntryChanges in changes)
17211722
{
1722-
RemoveFromIndex(treeEntryChanges.Path);
1723+
switch (treeEntryChanges.Status)
1724+
{
1725+
case ChangeKind.Conflicted:
1726+
if (!treeEntryChanges.Exists)
1727+
{
1728+
RemoveFromIndex(treeEntryChanges.Path);
1729+
}
1730+
break;
1731+
1732+
case ChangeKind.Deleted:
1733+
RemoveFromIndex(treeEntryChanges.Path);
1734+
break;
1735+
1736+
default:
1737+
continue;
1738+
}
17231739
}
17241740

17251741
foreach (TreeEntryChanges treeEntryChanges in changes)
@@ -1731,6 +1747,13 @@ public void Stage(IEnumerable<string> paths, StageOptions stageOptions)
17311747
AddToIndex(treeEntryChanges.Path);
17321748
break;
17331749

1750+
case ChangeKind.Conflicted:
1751+
if (treeEntryChanges.Exists)
1752+
{
1753+
AddToIndex(treeEntryChanges.Path);
1754+
}
1755+
break;
1756+
17341757
default:
17351758
continue;
17361759
}

LibGit2Sharp/TreeChanges.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class TreeChanges : IEnumerable<TreeEntryChanges>
2525
private readonly List<TreeEntryChanges> unmodified = new List<TreeEntryChanges>();
2626
private readonly List<TreeEntryChanges> renamed = new List<TreeEntryChanges>();
2727
private readonly List<TreeEntryChanges> copied = new List<TreeEntryChanges>();
28+
private readonly List<TreeEntryChanges> conflicted = new List<TreeEntryChanges>();
2829

2930
private readonly IDictionary<ChangeKind, Action<TreeChanges, TreeEntryChanges>> fileDispatcher = Build();
3031

@@ -39,6 +40,7 @@ private static IDictionary<ChangeKind, Action<TreeChanges, TreeEntryChanges>> Bu
3940
{ ChangeKind.Unmodified, (de, d) => de.unmodified.Add(d) },
4041
{ ChangeKind.Renamed, (de, d) => de.renamed.Add(d) },
4142
{ ChangeKind.Copied, (de, d) => de.copied.Add(d) },
43+
{ ChangeKind.Conflicted, (de, d) => de.conflicted.Add(d) },
4244
};
4345
}
4446

@@ -146,6 +148,14 @@ public virtual IEnumerable<TreeEntryChanges> Unmodified
146148
get { return unmodified; }
147149
}
148150

151+
/// <summary>
152+
/// List of <see cref="TreeEntryChanges"/> which are conflicted
153+
/// </summary>
154+
public virtual IEnumerable<TreeEntryChanges> Conflicted
155+
{
156+
get { return conflicted; }
157+
}
158+
149159
private string DebuggerDisplay
150160
{
151161
get

LibGit2Sharp/TreeEntryChanges.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ internal TreeEntryChanges(GitDiffDelta delta)
2525
OldMode = (Mode)delta.OldFile.Mode;
2626
Oid = delta.NewFile.Id;
2727
OldOid = delta.OldFile.Id;
28+
Exists = (delta.NewFile.Flags & GitDiffFlags.GIT_DIFF_FLAG_EXISTS) != 0;
29+
OldExists = (delta.OldFile.Flags & GitDiffFlags.GIT_DIFF_FLAG_EXISTS) != 0;
2830

2931
Status = (delta.Status == ChangeKind.Untracked || delta.Status == ChangeKind.Ignored)
3032
? ChangeKind.Added
@@ -46,6 +48,17 @@ internal TreeEntryChanges(GitDiffDelta delta)
4648
/// </summary>
4749
public virtual ObjectId Oid { get; private set; }
4850

51+
/// <summary>
52+
/// The file exists in the new side of the diff.
53+
/// This is useful in determining if you have content in
54+
/// the ours or theirs side of a conflict. This will
55+
/// be false during a conflict that deletes both the
56+
/// "ours" and "theirs" sides, or when the diff is a
57+
/// delete and the status is
58+
/// <see cref="ChangeType.Deleted"/>.
59+
/// </summary>
60+
public virtual bool Exists { get; private set; }
61+
4962
/// <summary>
5063
/// The kind of change that has been done (added, deleted, modified ...).
5164
/// </summary>
@@ -66,6 +79,16 @@ internal TreeEntryChanges(GitDiffDelta delta)
6679
/// </summary>
6780
public virtual ObjectId OldOid { get; private set; }
6881

82+
/// <summary>
83+
/// The file exists in the old side of the diff.
84+
/// This is useful in determining if you have an ancestor
85+
/// side to a conflict. This will be false during a
86+
/// conflict that involves both the "ours" and "theirs"
87+
/// side being added, or when the diff is an add and the
88+
/// status is <see cref="ChangeType.Added"/>.
89+
/// </summary>
90+
public virtual bool OldExists { get; private set; }
91+
6992
private string DebuggerDisplay
7093
{
7194
get

0 commit comments

Comments
 (0)