diff --git a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config
index 78387c50b..277bd5530 100644
--- a/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config
+++ b/LibGit2Sharp.Tests/Resources/mergedrepo_wd/dot_git/config
@@ -6,3 +6,4 @@
symlinks = false
ignorecase = true
hideDotFiles = dotGitOnly
+ autocrlf = false
diff --git a/LibGit2Sharp.Tests/StageFixture.cs b/LibGit2Sharp.Tests/StageFixture.cs
index 534383953..3118a6237 100644
--- a/LibGit2Sharp.Tests/StageFixture.cs
+++ b/LibGit2Sharp.Tests/StageFixture.cs
@@ -334,5 +334,43 @@ public void CanStageIgnoredPaths(string path)
Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(path));
}
}
+
+ [Theory]
+ [InlineData("new_untracked_file.txt", FileStatus.Ignored)]
+ [InlineData("modified_unstaged_file.txt", FileStatus.ModifiedInIndex)]
+ public void IgnoredFilesAreOnlyStagedIfTheyreInTheRepo(string filename, FileStatus expected)
+ {
+ var path = SandboxStandardTestRepoGitDir();
+ using (var repo = new Repository(path))
+ {
+ File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"),
+ String.Format("{0}\n", filename));
+
+ repo.Stage(filename);
+ Assert.Equal(expected, repo.RetrieveStatus(filename));
+ }
+ }
+
+ [Theory]
+ [InlineData("ancestor-and-ours.txt", FileStatus.Unaltered)]
+ [InlineData("ancestor-and-theirs.txt", FileStatus.NewInIndex)]
+ [InlineData("ancestor-only.txt", FileStatus.Nonexistent)]
+ [InlineData("conflicts-one.txt", FileStatus.ModifiedInIndex)]
+ [InlineData("conflicts-two.txt", FileStatus.ModifiedInIndex)]
+ [InlineData("ours-only.txt", FileStatus.Unaltered)]
+ [InlineData("ours-and-theirs.txt", FileStatus.ModifiedInIndex)]
+ [InlineData("theirs-only.txt", FileStatus.NewInIndex)]
+ public void CanStageConflictedIgnoredFiles(string filename, FileStatus expected)
+ {
+ var path = SandboxMergedTestRepo();
+ using (var repo = new Repository(path))
+ {
+ File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"),
+ String.Format("{0}\n", filename));
+
+ repo.Stage(filename);
+ Assert.Equal(expected, repo.RetrieveStatus(filename));
+ }
+ }
}
}
diff --git a/LibGit2Sharp/ChangeKind.cs b/LibGit2Sharp/ChangeKind.cs
index c95095a37..304438be8 100644
--- a/LibGit2Sharp/ChangeKind.cs
+++ b/LibGit2Sharp/ChangeKind.cs
@@ -55,5 +55,10 @@ public enum ChangeKind
/// Entry is unreadable.
///
Unreadable = 9,
+
+ ///
+ /// Entry is currently in conflict.
+ ///
+ Conflicted = 10,
}
}
diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs
index 1c71fbb5c..ab9f691db 100644
--- a/LibGit2Sharp/Core/GitDiff.cs
+++ b/LibGit2Sharp/Core/GitDiff.cs
@@ -231,6 +231,7 @@ internal enum GitDiffFlags
GIT_DIFF_FLAG_BINARY = (1 << 0),
GIT_DIFF_FLAG_NOT_BINARY = (1 << 1),
GIT_DIFF_FLAG_VALID_ID = (1 << 2),
+ GIT_DIFF_FLAG_EXISTS = (1 << 3),
}
[StructLayout(LayoutKind.Sequential)]
diff --git a/LibGit2Sharp/FileStatus.cs b/LibGit2Sharp/FileStatus.cs
index 191972f35..b07323449 100644
--- a/LibGit2Sharp/FileStatus.cs
+++ b/LibGit2Sharp/FileStatus.cs
@@ -125,5 +125,10 @@ public enum FileStatus
/// The file is but its name and/or path matches an exclude pattern in a gitignore file.
///
Ignored = (1 << 14), /* GIT_STATUS_IGNORED */
+
+ ///
+ /// The file is due to a merge.
+ ///
+ Conflicted = (1 << 15), /* GIT_STATUS_CONFLICTED */
}
}
diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs
index 79c9077bc..4ab882eb8 100644
--- a/LibGit2Sharp/Repository.cs
+++ b/LibGit2Sharp/Repository.cs
@@ -1656,6 +1656,7 @@ public void Stage(IEnumerable paths, StageOptions stageOptions)
.Where(
tec => tec.Status != ChangeKind.Added &&
tec.Status != ChangeKind.Modified &&
+ tec.Status != ChangeKind.Conflicted &&
tec.Status != ChangeKind.Unmodified &&
tec.Status != ChangeKind.Deleted).ToList();
@@ -1667,10 +1668,25 @@ public void Stage(IEnumerable paths, StageOptions stageOptions)
unexpectedTypesOfChanges[0].Path, unexpectedTypesOfChanges[0].Status));
}
- foreach (TreeEntryChanges treeEntryChanges in changes
- .Where(tec => tec.Status == ChangeKind.Deleted))
+ /* Remove files from the index that don't exist on disk */
+ foreach (TreeEntryChanges treeEntryChanges in changes)
{
- RemoveFromIndex(treeEntryChanges.Path);
+ switch (treeEntryChanges.Status)
+ {
+ case ChangeKind.Conflicted:
+ if (!treeEntryChanges.Exists)
+ {
+ RemoveFromIndex(treeEntryChanges.Path);
+ }
+ break;
+
+ case ChangeKind.Deleted:
+ RemoveFromIndex(treeEntryChanges.Path);
+ break;
+
+ default:
+ continue;
+ }
}
foreach (TreeEntryChanges treeEntryChanges in changes)
@@ -1682,6 +1698,13 @@ public void Stage(IEnumerable paths, StageOptions stageOptions)
AddToIndex(treeEntryChanges.Path);
break;
+ case ChangeKind.Conflicted:
+ if (treeEntryChanges.Exists)
+ {
+ AddToIndex(treeEntryChanges.Path);
+ }
+ break;
+
default:
continue;
}
diff --git a/LibGit2Sharp/TreeChanges.cs b/LibGit2Sharp/TreeChanges.cs
index 385770875..ae3fd38cc 100644
--- a/LibGit2Sharp/TreeChanges.cs
+++ b/LibGit2Sharp/TreeChanges.cs
@@ -25,6 +25,7 @@ public class TreeChanges : IEnumerable
private readonly List unmodified = new List();
private readonly List renamed = new List();
private readonly List copied = new List();
+ private readonly List conflicted = new List();
private readonly IDictionary> fileDispatcher = Build();
@@ -39,6 +40,7 @@ private static IDictionary> Bu
{ ChangeKind.Unmodified, (de, d) => de.unmodified.Add(d) },
{ ChangeKind.Renamed, (de, d) => de.renamed.Add(d) },
{ ChangeKind.Copied, (de, d) => de.copied.Add(d) },
+ { ChangeKind.Conflicted, (de, d) => de.conflicted.Add(d) },
};
}
@@ -146,6 +148,14 @@ public virtual IEnumerable Unmodified
get { return unmodified; }
}
+ ///
+ /// List of which are conflicted
+ ///
+ public virtual IEnumerable Conflicted
+ {
+ get { return conflicted; }
+ }
+
private string DebuggerDisplay
{
get
diff --git a/LibGit2Sharp/TreeEntryChanges.cs b/LibGit2Sharp/TreeEntryChanges.cs
index 205ff42c3..bb288d265 100644
--- a/LibGit2Sharp/TreeEntryChanges.cs
+++ b/LibGit2Sharp/TreeEntryChanges.cs
@@ -25,6 +25,8 @@ internal TreeEntryChanges(GitDiffDelta delta)
OldMode = (Mode)delta.OldFile.Mode;
Oid = delta.NewFile.Id;
OldOid = delta.OldFile.Id;
+ Exists = (delta.NewFile.Flags & GitDiffFlags.GIT_DIFF_FLAG_EXISTS) != 0;
+ OldExists = (delta.OldFile.Flags & GitDiffFlags.GIT_DIFF_FLAG_EXISTS) != 0;
Status = (delta.Status == ChangeKind.Untracked || delta.Status == ChangeKind.Ignored)
? ChangeKind.Added
@@ -46,6 +48,17 @@ internal TreeEntryChanges(GitDiffDelta delta)
///
public virtual ObjectId Oid { get; private set; }
+ ///
+ /// The file exists in the new side of the diff.
+ /// This is useful in determining if you have content in
+ /// the ours or theirs side of a conflict. This will
+ /// be false during a conflict that deletes both the
+ /// "ours" and "theirs" sides, or when the diff is a
+ /// delete and the status is
+ /// .
+ ///
+ public virtual bool Exists { get; private set; }
+
///
/// The kind of change that has been done (added, deleted, modified ...).
///
@@ -66,6 +79,16 @@ internal TreeEntryChanges(GitDiffDelta delta)
///
public virtual ObjectId OldOid { get; private set; }
+ ///
+ /// The file exists in the old side of the diff.
+ /// This is useful in determining if you have an ancestor
+ /// side to a conflict. This will be false during a
+ /// conflict that involves both the "ours" and "theirs"
+ /// side being added, or when the diff is an add and the
+ /// status is .
+ ///
+ public virtual bool OldExists { get; private set; }
+
private string DebuggerDisplay
{
get