Finding hidden files and folders on a Mac

A repository needs checking, a .env has gone missing, or a .zshrc needs editing, and the folder that should contain it looks empty. Searches for find .git file mac usually start there. Two things make the problem more confusing than it should be. Hidden is not one condition on macOS, it is three, and the graphical search and the shell disagree about what the word covers. Sorting out those two points turns an annoying hunt into a couple of keystrokes.

Three different reasons a file is hidden

The first category is names that begin with a dot: .zshrc, .gitignore, .env, and the .git directory itself. This is a Unix convention rather than a macOS feature. There is no attribute involved. The leading period is the entire mechanism, which is why renaming such a file makes it appear immediately.

The second category is items carrying a hidden flag in the file system. Top level system directories such as /usr, /bin, and /private sit here. Their names contain no dot, but a Finder window will not display them.

The third category is folders hidden by configuration rather than by a flag. The clearest example is ~/Library, which holds application settings, caches, containers, and saved state, and which is not shown by default in a home directory listing.

Separating the three matters because the fixes differ. One keyboard shortcut reveals the first two categories at once. The third is controlled through a view setting and behaves differently. A large share of the time spent on this problem comes from applying the shortcut to ~/Library and concluding, wrongly, that the shortcut is broken.

Revealing them in a Finder window

The shortest route is Cmd+Shift+. with a Finder window in front. Hidden items appear, dimmed, and the same keys hide them again. Nothing is written to a preference file, so this is a temporary look rather than a configuration change.

The second route is typing the path. Cmd+Shift+G opens an input field that accepts a path directly, and the destination opens whether or not it is hidden. Apple documents the behaviour plainly.

If you know the exact name and location of a folder, you can also open it by entering the pathname. Source: support.apple.com

That field expands a tilde, so ~/.ssh and ~/Library/Application Support both work without revealing anything first. When the destination is known, this beats scanning a dimmed list.

The third route covers the library folder. With a home folder window open, holding Option while opening the View menu adds a Library entry to the list. To keep it visible permanently, open the home folder, choose Show View Options from the View menu, and enable the option to show the Library folder.

That field also completes as it is typed. Pressing Tab after a partial path fills in the rest, so ~/Li becomes the library path without any of it being remembered exactly. For deeply nested application support folders, the completion removes most of the opportunity for a typo.

One more behaviour is worth knowing, because it produces a contradiction that looks like a bug. The reveal shortcut also works inside an application's open and save dialogs, but the state is local to that dialog. Hidden files can be visible in a save sheet and invisible in a Finder window at the same moment, and neither setting affects the other.

Search is a separate setting again. Revealing hidden items in a window does not add them to search results. Start a search in a Finder window, use the plus button to add a criterion, choose Other, and select the attribute that controls system files. Without that criterion, hidden files stay out of the result list no matter how the query is written.

Finding them from a terminal

From the shell there is nothing special about a hidden file. ls -a lists dot entries, and find includes them by default because it simply walks directory entries.

find ~/work -maxdepth 4 -name ".env" 2>/dev/null
find ~ -maxdepth 3 -type d -name ".git" 2>/dev/null

Quoting the pattern keeps the shell from expanding it first. -maxdepth bounds the traversal, which matters on a home directory holding several project trees, and 2>/dev/null discards permission warnings that would otherwise bury the matches.

mdfind is the tool to be careful with here. The Spotlight index generally does not ingest dot files or system areas, so an index query for a hidden file often returns nothing at all. That empty result is not evidence of absence. Switching to find answers the question properly, and mdls filename shows whether the index holds anything for a given file.

Content search has the same split. The grep shipped with macOS reads whatever it walks past. ripgrep and fd skip dot files and anything matched by a .gitignore unless told otherwise.

rg --hidden -uu "AWS_SECRET" ~/work
fd -HI "^\.env" ~/work
Tool Dot files by default Ignore files respected Flag to include everything
find Included Not applicable Not needed
grep -r Included No Not needed
rg Skipped Yes --hidden -uu
fd Skipped Yes -HI

A string that grep finds and rg misses is this table, not a bug. Configuration files and credentials are exactly the files those defaults exclude, since every one of them begins with a dot.

What is different about looking for .git

As a name, .git is an ordinary dot entry. As a search target it is unusual, because the real question is normally not where the directory is. It is which folders on this machine are repositories.

Restricting the search to directories and bounding the depth answers that quickly. Walking a whole home directory without a depth limit descends into caches and dependency folders, which produces a long list of repositories nobody was asking about.

find ~ -maxdepth 4 -type d -name ".git" -exec dirname {} \; 2>/dev/null

Piping through dirname lists repository roots rather than the .git directories themselves, which is almost always the more useful output.

One detail catches people out. .git is not always a directory. Inside a submodule, or a checkout created with git worktree, it is a single line text file pointing at the real location. A search using -type d silently omits those. Dropping the type filter and checking the results afterwards catches both forms.

Depth is the other thing to watch. A .git directory inside a submodule sits one level below the parent repository, so a bounded search returns both, and the list needs reading with that in mind. Checking whether a result is nested inside another repository takes one look at the paths, and it prevents an inner project being treated as an independent one.

Deletion deserves care for the same reason. Removing a .git directory removes the history of that project, and a bulk cleanup written to tidy dot files can do that without warning. Reading a repository state does not require entering it: git -C path status reports on a repository from outside it.

Reading the state of every repository at once

Once the roots are listed, one more step usually answers the actual question. Running git -C path status --short against each root prints only the paths with uncommitted changes, which turns a list of forty repositories into a list of the three that were left mid-edit. On a machine used for client work, that is normally the reason the search started.

git -C path remote -v is the companion command. It prints where each repository is pushed to, which separates the projects that exist in more than one place from the ones that only exist on this disk. Doing that before any cleanup, backup, or disk migration is cheap, and it removes the guesswork about which folders are safe to archive.

Both commands read from outside the directory, so neither requires changing into it or opening anything. Combined with the bounded find above, the whole audit is two commands and a few seconds, rather than a manual tour of a projects folder.

There is a related trap in the depth limit. A repository nested four or five levels down, inside a folder of archived work, will be missed by -maxdepth 4. Running the search once without a depth limit, accepting that it takes longer, gives a complete list to compare against. After that, the bounded version is enough for daily use.

Whether to leave hidden files showing

The shortcut can be left in the on position, and that has a cost. A home folder and every active project folder fill up with .DS_Store entries, lock files, and editor state, which makes the files actually being worked on harder to pick out. The busier the folder, the worse the effect.

There is a second cost that shows up later. With everything visible, drag and drop selections inside a busy folder start picking up metadata files by accident, and a copy that was meant to move six documents carries a pile of state files with it into a shared drive. The files are harmless, but explaining them to someone on the receiving end is not free.

Toggling on demand is the more workable habit. The keystroke is one action, it changes no settings, and the same keystroke reverses it. Leave hidden items showing for a day spent editing configuration, then turn them off.

A quick way to see what is actually there is ls -la ~ | head -30, which prints the dot entries sitting directly in a home directory. Shell configuration, editor state, cloud storage metadata, and credentials for several command line tools usually turn out to share that one level. Seeing the list once makes later searches far more directed, because the guessing about where a given tool keeps its settings mostly stops.

The same reasoning applies in the shell. Writing --hidden into an rg configuration file makes it permanent, and every search then reads the contents of .git directories, returning dozens of matches for a string that appears once in the source. Passing the flag when it is needed keeps the output short.

Where the time actually goes

Work on hidden files is a loop of checking and editing. Confirm a location in a folder window, open a terminal to edit, then confirm the result again. A reveal toggle and a window switch sit inside every pass of that loop.

That is a layout problem rather than a search problem. When the file list and the command line share one working directory, hidden items can stay visible in the list while commands run against them in the same view, and the switching disappears. What that arrangement looks like in practice is described on the Features page, and the differences from dual pane browsers and terminal-first tools are set out on Compared with other file managers.

What to change first

Write down the two commands above and stop re-deriving them: a bounded find for dot files, and rg --hidden -uu for contents. If the loop of switching windows around them is what actually costs the time, that is worth solving separately, and Atriens is one example of the shape of tool that removes it.

Frequently asked questions

Why does Cmd+Shift+. not reveal the Library folder?

The library folder is hidden by a view setting rather than by the hidden flag, so the shortcut does not cover it. Hold Option while opening the View menu in a home folder window to reach it once, or open Show View Options from the View menu and enable the option to show the Library folder permanently.

How do you get hidden files to appear in Finder search results?

Revealing them in a window is a separate setting from including them in a search. Start the search, click the plus button to add a criterion, choose Other, and pick the attribute that controls system files. From a terminal no setting is needed, because find includes dot entries by default.

Why does mdfind return nothing for a hidden file that exists?

The Spotlight index generally does not ingest dot files or system directories, so an index query has nothing to match. An empty result is a statement about the index, not about the disk. Running find ~ -name ".env" walks the file system directly and will find the file regardless of index state.

Is it safe to delete a .git directory to save space?

Deleting it removes the entire history of that project, including any commits that were never pushed anywhere else. Space inside a repository is more safely reclaimed with git's own maintenance commands. To inspect a repository without opening it, git -C path status reports on it from outside the directory.

Back to all posts