I must have read at least 50 StackOverflow questions and answers that say that Git cannot track directories. And yet, that is exactly what seems to be happening.
I created a project (.NET, on Windows), and added and committed all the files prior to adding a .gitignore. Realizing my mistake later on, I git rm -r --cached :/ everything, added this .gitignore, and the re-added and committed my files. The thing is, git still tracks my obj and bin folders even though they seem to be ignored in the .gitignore.
Here are the relevant lines from the .gitignore:
[Bb]in/
[Oo]bj/
bin/**
obj/**
One or two of those might not make sense, I'm not totally familiar with .gitignore rules and was just trying to see what would stick.
Here's what I get for git status:
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/main/dotnet/ETB/ETB.Droid/bin/
src/main/dotnet/ETB/ETB.Droid/obj/
src/main/dotnet/ETB/ETB.iOS/bin/
src/main/dotnet/ETB/ETB.iOS/obj/
src/main/dotnet/ETB/ETB/bin/
src/main/dotnet/ETB/ETB/obj/
src/main/dotnet/packages/
This is even after I do something like git rm -r --cached .\src\main\dotnet\ETB\ETB.Droid\bin from the root level. There are also ZERO tracked files from within these directories that appear in the "Changes not staged for commit" section when I do a git status.
I'm really, really stumped. Can anyone help me figure out why I can't ignore these directories completely?
Update
I made the changes that the commenters suggested, and it seemed to solve some, but not all, of my problems (sorry I had it marked answered for a bit there). Relevant lines in my .gitignore at the root level are:
**/[Dd]ebug/**
**/bin/**
**/obj/**
That first line is probably not necessary, but I figured it couldn't hurt. There is definitely no extra whitespace on any of these lines.
For some reason, only one of the obj directories is still showing up in Git. I even deleted and re-added everything just to try it out.
The offending directory is the ETB.Data directory:
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/main/dotnet/ETB.Data/
So I ran this command:
git rm -r --cached .\src\main\dotnet\
I then committed those deletes. Then I tried to re-add the directory
git add .\src\main\dotnet
When I look at my status, here is what I'm seeing:
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: src/main/dotnet/ETB.Data/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs
new file: src/main/dotnet/ETB.Data/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs
new file: src/main/dotnet/ETB.Data/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs
new file: src/main/dotnet/ETB.sln
...
...
Why do these files keep showing up?! The obj and bin directories in other project directories are being ignored. Does anyone know why this one isn't being ignored?
You need to tell git to ignore all the bin/obj files/folders, not just the ones at its root :
**/bin/**
**/obj/**
From man gitignore :
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".
A trailing "/**" matches everything inside. For example, "abc/**" matches all files inside directory "abc", relative to
the location of the .gitignore file, with infinite depth.
Thats very simple because your line in your .gitignore file are not correct. I can't test it now but try something like this for example
**/bin/**
**/obj/**
When you don't write the * at the beginning your line is interpreted as the start.
There is a good comment if you read the man page.
. 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".
. A trailing "/" matches everything inside. For example, "abc/"
matches all files inside directory "abc", relative to the location of
the .gitignore file, with infinite depth.
Related
How would one go about only listing the png files that have been modified in the current branch on Git?
My goal is to copy those files to a different directory (I need to send an email).
Suppose I have:
$ git status
On branch update_assessment_pt1
Your branch is up-to-date with 'upstream/devel'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: assessment/LWR/validation/HbepR1/analysis/hbepr1_plot.py
deleted: assessment/LWR/validation/HbepR1/doc/figures/AxialPowerProfile.pdf
deleted: assessment/LWR/validation/HbepR1/doc/figures/AxialProfile.pdf
deleted: assessment/LWR/validation/HbepR1/doc/figures/CladDisp.pdf
deleted: assessment/LWR/validation/HbepR1/doc/figures/FissionGas.pdf
modified: assessment/LWR/validation/HbepR1/doc/figures/FissionGas.png
deleted: assessment/LWR/validation/HbepR1/doc/figures/InterGasPress.pdf
deleted: assessment/LWR/validation/HbepR1/doc/figures/Mesh.pdf
deleted: assessment/LWR/validation/HbepR1/doc/figures/Power.pdf
modified: assessment/LWR/validation/HbepR1/doc/figures/Power.png
new file: assessment/LWR/validation/IFA_431/analysis/ifa431_plot.py
modified: assessment/LWR/validation/IFA_431/doc/figures/431_bol_rod_power.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431r1.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431r2.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431r3.png
How would I go about getting the following, so I can copy those files?
modified: assessment/LWR/validation/HbepR1/doc/figures/FissionGas.png
modified: assessment/LWR/validation/HbepR1/doc/figures/Power.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431_bol_rod_power.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431r1.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431r2.png
modified: assessment/LWR/validation/IFA_431/doc/figures/431r3.png
Use git diff --cached --diff-filter=M --name-only to obtain these file names. Add -- '*.png' if needed to keep the list filtered to just *.png files—the command will list any to be committed file whose status is M (modified).
Things to know to keep this from just being a "use this magic command" answer
In text, you first called these modified in the current branch. This phrase doesn't mean any one specific thing. Fortunately you then went on to show git status output, where they were listed under Changes to be committed.
Git doesn't store diffs at all. Git stores snapshots—whole files, intact, inside the main unit of storage, which is the commit. That means that in order to see a change, you have to pick two commits: $old and $new. Git will extract both, then compare them. Whatever is different between commit $old and commit $new, Git will tell you about that. The actual change can be any of a number of change-status-es:
A means Added: the file is not in $old and is in $new.
M means Modified: the file is different between $old and $new. The difference could just be the mode of the file: executable, or not.
D means Deleted: the file is in $old, but not in $new.
R, C, T, and some other rare cases can also occur, though some of them may require extra flags to git diff: you won't see an R status unless you enable rename-detection, for instance. (Rename detection defaults to on in the most modern Git versions, but off in older Git versions.)
Using --name-status, git diff will show you the file names and status letters, instead of showing an actual diff. (Try this out to see.) The --diff-filter argument lets you tell Git: only tell me about files whose status meets the letters I pick.
Note, by the way, that the special name HEAD always means the current commit. It does not matter how you made this commit become the current commit, though one typical way is by using git checkout: you git checkout a commit by its hash ID, for instance, and that commit is now checked out and is the current commit. Or, you git checkout a branch name, and the tip commit of that branch is now out and is the current commit. There is always1 a current commit, and you can name it by writing the name HEAD in all uppercase.2
All of the above talks about comparing commits, but there are two other places that files can exist, that are not commits. Note that both of these places are temporary: they get wiped out by various operations, and once wiped out, cannot be recovered in Git: you have to copy from these temporary places, into actual commits, to make the files permanent. Once the files are in commits, they're frozen for all time, and can be restored to useful form in the future for as long as the commit itself exists (which tends to be "forever", or as long as the repository exists).
These two places are:
the index, which Git also calls the staging area or (rarely) the cache, and
the work-tree or working tree or any of several variants on this name.
Files that are in the index right now are ready to be committed. Every file that will be committed is in the index right now, even if the index copy matches the current (HEAD) commit copy.
You can, at any time, compare the HEAD commit to whatever is in the index right now. One command that does this is git diff --cached. For every file in HEAD and/or in the index, Git compares the two copies of the file. If they are different, the file is modified. If the index file exists but there is no such file in HEAD, the file is added. If the file exists in HEAD but not in the index, the file is deleted.
You can also, at any time, compare HEAD to the work-tree, or the index to the work-tree. The commands that do this are git diff HEAD and git diff (with no name). Again, for every file on the left-hand side (HEAD or the index), and every file on the right-hand side (in the work-tree), Git compares the two copies of the file.
Last, note that git status runs two git diffs. It does a quick git diff --cached to compare HEAD vs index. Whatever is different here, git status lists that file as to be committed. It also does a quick git diff (with no extra arguments except for --name-only) to compare index vs work-tree. Whatever is different here, git status lists that file as changes not staged for commit.
You wanted to compare HEAD vs index, so you want git diff --cached. You then wanted to list only those files that are Modified, so you can add --diff-filter=M. You didn't want to see the actual differences—nor even the status letters; file names only please!—so you can add --name-only. You also wanted only to list files whose name matches *.png, so add -- '*.png'—the quotes protect the * from the shell; we want Git to see the * so that Git can treat it as a pathspec—to get just those.
1Actually, this is really almost always. There's a special state in which HEAD exists and contains a branch name, but the branch name itself doesn't exist. This state mostly occurs when you create a new, totally-empty repository. Git requires a branch name like master to identify some existing, valid commit hash ID. There are no commits, so there are no valid hash IDs, so master itself is not allowed to exist. Nonetheless, HEAD holds the name master, so that Git will create the master branch when you make the first commit.
2On Windows and MacOS, you can sometimes get away with using head (lowercase) instead of HEAD (all-uppercase). This misbehaves if you start using git worktree add, so it's a bad habit to get into. If you don't like typing HEAD in all capitals, consider using the symbol #, which is a synonym for HEAD.
How do I git add a file in a different directory using relative path?
Say I have 2 files in 2 different directories:
somerepo/a/fileina and somerepo/b/fileinb.
For example, if I'm currently in directory "a" (user:~/somerepo/a) and I want to use git add to stage changes in "fileinb" using a relative path. what the command line looks like?
Should I type:"git add ./b/fileb" or "git add ./../b/fileb". I'm confused about what . in git means.
Thanks
In virtually all places in Git where you specify a path, . means the same thing as anywhere else: the current/same directory. So, ./b is equivalent to just b, and x/./y is equivalent to x/y.
In your case, you'd want to go one directory up, meaning your second suggestion is correct, but you don't need the leading ./, you can just say git add ../b/fileb.
Based on this post and this post ,
git ls-files --others --exclude-standard can list all untracked files.
But I test it, cannot list empty folder(both tracked and not tracked).
For example,cannot list empty folder archiver folder as below:
.
├── admin.php
├── api
│ ├── index.htm
│ └── remote
│ └── mod
│ ├── index.htm
│ ├── mod_cron.php
│ └── mod_index.php
└── archiver folder
Then my question is: how to list all untracked files and empty folders?
TL;DR: just look for empty directories. You can safely remove them—well, "safe" depends on your own software, but as far as Git is concerned, it's safe. (Watch out for missing files—see the definition of "missing" below—which may remove a directory that Git might want later, but that's sort of OK, because Git will just create it again.)
On a Unix / Linux system (edited to correct lost word in transcription):
find . -name .git -prune -o -type d -empty -print
(at the top level of the work-tree) will find the empty directories.
Long(ish)
Git is not interested in folders / directories. There's no such thing as an untracked folder in the same way that there's no such thing as a tracked folder: Git only cares about files. Specifically, a file is either in the index, or not in the index, and if it's not in the index, it's untracked.
When you use the various options to list untracked files (which tend to skip over ones that are untracked-and-ignored since you normally want that), Git will, sometimes, aggregate together all the files that are in some folder, notice that there are no tracked files in that folder, and report them using the aggregated notation. You can stop this with, e.g., git status --untracked-mode=all; then you'll get the individual file names.
Note that it's possible to have some file that is tracked, yet missing. For instance, suppose sub/README.txt is a tracked file, and actually exists. Then we run rm sub/README.txt. The file sub/README.txt remains in Git's index, and will be in the next commit, but it's missing. If that was the only file in sub in your work-tree, sub is now empty, and you can remove it with rmdir sub. Even though sub/README.txt remains missing (and sub is missing too!), that does not affect the next commit: it will still contain sub/README.txt, because that file is in the index. (Using git rm --cached sub/README.txt, you can remove it from the index too, if that's what you wanted.)
If and when Git goes to copy sub/README.txt back out of the index into the work-tree, Git will, at this point, discover that there is no sub. Git will merely shrug its metaphorical shoulders and create the directory sub, and then put sub/README.txt into it. So this is why Git is not interested in folders / directories: they're just boring and dull, required only when needed to hold files, created on demand.
If you want Git to create a directory, you need to store a file in it. Since programs managed by Git need to be able to ignore the file named .gitignore, this is a very good file name to stick into such a directory. You can write * into that file, and add it to your commits, so that Git will create the directory and write a .gitignore file there containing *, and will thus ignore all additional untracked files within that directory automatically.
Side note: In general, when Git pulls the last file out of some directory, it will remove the directory too, but occasionally I've seen it leave some behind. (Of course, it has to leave the directory behind if it still contains some untracked files. Note that git clean -fd will remove the empty directories, though it also removes the untracked files.)
git ls-files --others --exclude-standard> not_tracked
find . -depth -empty -type d \( ! -regex '.*/\..*' \) >> not_tracked
Please check my answer,I spent 2 days for it.
The command git clean does exactly what you want.
I have the .gitignore file with this code:
/app/cache/*
/app/logs/*
/app/bootstrap*
/vendor/*
/web/bundles/
/app/config/parameters.yml
but when I do :
$ git status
in any situation (before and after add and commit), I get a long text output like this:
...
# deleted: vendor/doctrine/orm/tools/sandbox/cli-config.php
# deleted: vendor/doctrine/orm/tools/sandbox/doctrine
# deleted: vendor/doctrine/orm/tools/sandbox/doctrine.php
# deleted: vendor/doctrine/orm/tools/sandbox/index.php
# deleted: vendor/doctrine/orm/tools/sandbox/xml/Entities.Address.dcm.xml
# deleted: vendor/doctrine/orm/tools/sandbox/xml/Entities.User.dcm.xml
# deleted: vendor/doctrine/orm/tools/sandbox/yaml/Entities.Address.dcm.yml
# deleted: vendor/doctrine/orm/tools/sandbox/yaml/Entities.User.dcm.yml
# modified: vendor/friendsofsymfony/user-bundle/FOS/UserBundle
# modified: vendor/gedmo/doctrine-extensions
# modified: vendor/herzult/forum-bundle/Herzult/Bundle/ForumBundle
# modified: vendor/kriswallsmith/assetic
# modified: vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore
# modified: vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php
# modified: vendor/symfony/symfony/CHANGELOG-2.1.md
...
The vendors directory is in .gitignore file, so I don't know what is happening.
I've tried with:
$ sudo git clean -dxf
but nothing changes.
Your vendor directory is checked in to the repo. To remove it, go to your git repo root and run:
git rm -r --cached vendor
This will recursively (due to -r flag) remove vendor from your git repo. The --cached flag will keep the local copy of vendor directory in tact. Note that if there are other devs working with the repo, their copy of the vendor directory will be removed and they will need to bundle install again.
Once you've untracked the directory in git, you can commit the change using:
git commit -m "untrack vendor directory"
Thereafter, .gitignore will happily ignore any changes within the vendor directory next time onwards.
Also, you don't need your entries in .gitignore to begin with a /. Use the / when you want to ensure that only files/folders in root directory are ignored, and any file in a subdirectory matching the pattern should not be ignored.
It looks like you already have files under vendor/* checked in. .gitignore ignores only untracked files. See also the first paragraph in man gitignore.
I create .gitignore in folder with my repository near .git
project
--.git
--.gitignore
--Source
----tmp
----scr
But git doesnt see it, wouldnt ignore files in .gitignore
My .gitignore file:
*.tmp
*~
*.pdb
*.user
*.suo
Sources/tmp
What`s wrong?
Up:
I created new repositiry, add .gitignore before init commit - it work!
But if I add in old repository it doesn`t...
The problem is that you're specifying glob syntax when the default syntax for git is regex.
Try this instead:
.*\.tmp
.*~
.*\.pdb
.*\.user
.*\.suo
Sources\/tmp
What you have should work, though your directory listing has Source/ while your .gitignore has Sources/.
The one thing that springs to mind is that the line endings might not be what git is expecting.
Also, as tmp is a directory, usually a trailing '/' is used:
Source/tmp/
Finally, you can also create a .gitignore in Source/ with the line:
tmp/
instead of having it in the top directory.