- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2.5k
Description
When there is a gitignore entry with a trailing slash, git_index_add_all will add symlinks that match the gitignore patterns on Windows but not other platforms.
Reproduction steps
The following Rust program demonstrates the problem. Sorry for only including a Rust reproduction, hopefully it is easy to see the equivalent C api calls (it is mostly 1:1). I couldn't figure out how to build a project with Visual Studio linking to libgit2 (if there are docs somewhere on how to do that, I'd be happy to make a C example).
use git2::*;
use std::fs;
fn main() {
    // Create a repo with a gitignore file and a symlink.
    fs::create_dir_all("repo/src").unwrap();
    fs::write("repo/src/samplefile", "test").unwrap();
    #[cfg(windows)]
    {
        std::os::windows::fs::symlink_dir("src", "repo/src2").unwrap();
    }
    #[cfg(unix)]
    {
        std::os::unix::fs::symlink("src", "repo/src2").unwrap();
    }
    fs::write("repo/.gitignore", "/src2/").unwrap();
    let repo = Repository::init("repo").unwrap();
    // Add all and commit.
    let mut index = repo.index().unwrap();
    index
        .add_all(["*"], git2::IndexAddOption::DEFAULT, None)
        .unwrap();
    index.write().unwrap();
    let tree_id = index.write_tree().unwrap();
    let tree_oid = repo.find_tree(tree_id).unwrap();
    let sig = repo.signature().unwrap();
    let oid = repo
        .commit(Some("HEAD"), &sig, &sig, "initial commit", &tree_oid, &[])
        .unwrap();
    // Check the contents of the commit.
    let commit = repo.find_commit(oid).unwrap();
    let mut names = Vec::new();
    commit
        .tree()
        .unwrap()
        .walk(TreeWalkMode::PreOrder, |_name, entry| {
            names.push(entry.name().unwrap().to_string());
            TreeWalkResult::Ok
        })
        .unwrap();
    names.sort();
    assert_eq!(names, [".gitignore", "samplefile", "src"]);
}In this example, there is a file src/samplefile and a symlink src2 -> src.  The .gitignore has a pattern /src2/ intending to prevent src2 from being added.
Expected behavior
git_index_add_all will only add .gitignore and src/samplefile.
Actual behavior
On Windows, it also adds the src2 symlink, causing the final assert to fail.
This seems to only happen with patterns with a trailing slash. A summary of the the gitignore pattern behavior:
- /src2/and- src2/fail on Windows.
- /src2and- src2work as expected on all platforms.
I've also tested with core.symlinks=true or false, it doesn't seem to affect it.
Version of libgit2 (release number or SHA1)
Operating system(s) tested
Windows, macOS, Linux