Git adding .gitignored files from nested target, even after reset

50 Views Asked by At

My .gitignore file for a bunch of rust code:

# Generated by Cargo
# will have compiled files and executables
*/target/
*/target/*

# Performance stuff
**/*.data
**/*.data.old
**/*.svg

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

Beneath this are a number of rust projects, but none of them contain other git stuff:

$ find . -name .git
./.git
$ find . -name .gitignore
./.gitignore

The most recent changes include a nested rust project for some procedural macros - so my file tree looks like

|.gitignore
|<other projects>
|OneProject/
|--<other files>
|--NestedProject
|---- <other files>
|---- target/
|------ <lots of unwanted stuff getting added>

I noticed (during a push) that it was adding a ton of stuff from the target directory of the nested project. (at the time, my .gitignore only had */target/ - added the additional */target/* after.

I did a local git reset HEAD~1, modified the .gitignore file to what it is now, and tried adding again, but the files still showed up as added. I did git reset, then switched to a new branch (I don't want to force push out of general cowardice), and tried following the instructions from Git Ignores and Maven targets - i.e.:

git rm -rf --cached .
git add .
git commit -m "gitignore is now working"
git push

And still the projects under OneProject/NestedProject/target/ got pushed (confirmed by looking thru my browser).

What am I missing?

2

There are 2 best solutions below

2
dani-vta On BEST ANSWER

The reason why */target/ wasn't working is because a separator at the beginning of the path matches relatively to the folder of the gitignore, while a single asterisk matches only a single folder level. As the gitignore documentation states in the Pattern Format section:

If there is a separator at the beginning or middle (or both) of the pattern, then the pattern is relative to the directory level of the particular .gitignore file itself.

An asterisk "*" matches anything except a slash.

This means that if your target folder was under OneProject, then the exclusion */target/ would have worked, as target was under one subfolder level relatively to the gitignore's folder.

If your target folder will always be a couple of levels below the gitignore's folder, you can replace */target/ with */*/target/. You could also get rid of */target/*, as I assume that was a second attempt of ignoring all target's files.

Instead, to answer the question in the comments, a leading double asterisk preceding a slash matches across all directories, that's why **/target/ was working too.

A leading "**" followed by a slash means match in all directories. For example, "**/foo" matches file or directory "foo" anywhere, the same as pattern "foo". "**/foo/bar" matches file or directory "bar" anywhere that is directly under directory "foo".

Alternatively, you could also use target/. In fact, the trailing slash matches the given pattern only for directories, and since there is no slash at the beginning, the pattern is not applied relatively to the gitignore's folder, but at every level.

If there is a separator at the end of the pattern then the pattern will only match directories, otherwise the pattern can match both files and directories.

These last two solutions might be more viable, if your target folder might be moved somewhere else in the future, or if you're planning to have other target folders and that all of them should be ignored.

0
j6t On

The most sensible way to ignore all directories named target anywhere in the hierarchy is to have just this line in .gitignore:

target/

It means:

  • Since there is no slash at the beginning or in the middle, the name target is ignored at any level of the hierarchy (including the top-most level, top-most being the directory that contains this .gitignore).
  • Since there is a slash at the end, the name target is only ignored, if it is a directory.