25

When I git add a folder, the whole content and all subfolders are staged automatically. In case the folder contains subfolders which I do not want to commit, I have to unstage them manually and add them to .gitignore afterwards. The explicit unstaging feels like I'm doing something wrong here.

A solution would be to edit the .gitignore before adding. But in cases where the folder structure is very deep/complex this is a bit tricky, because it is easy to forget to ignore certain deeply nested files/folders.

What I was looking for is a step-wise add like SVN's --non-recursive, allowing to add folders level by level without staging the whole content. However I couldn't find this functionality for git add. So I'm wondering: What is the recommended git workflow for such a non-recursive add?

Considering that others had the exact opposite problem: Maybe the behavior I described above is an issue with my git version (1.9.1) / settings?

Community
  • 1
  • 1
bluenote10
  • 23,414
  • 14
  • 122
  • 178
  • 1
    If you have just few folders, you can add `.gitignore` to subfolders before you use `git add` – Raptor Jun 05 '15 at 10:18
  • 1
    @Raptor: I totally forgot that I can simply use `.gitignore` in the subdirectories, which is much easier than editing the central `.gitignore`. In combination with `--dry-run` and the possibility to just specify **files**, I consider my problem solved. – bluenote10 Jun 05 '15 at 12:22
  • 3
    Is there no way to do this in a more general way? None of the solutions below come close to giving me a useful solution. – LovesTha Jan 10 '16 at 23:38
  • @LovesTha: If you need this a lot, I guess you could create a git alias which translates `git add-non-recursive subfolder1 subfolder2 ...` into `git add $(find subfolder1 -maxdepth 1 -type f) $(find subfolder1 -maxdepth 1 -type f) ...`. – bluenote10 Jan 11 '16 at 07:35

4 Answers4

12

Adding a whole complex directory hierarchy is an unusual thing to do (and certainly not something that happens as part of the usual git development workflow), so git doesn't have a special feature for it. You can always use an external tool to assemble a list of files you want to add and feed that list to git add, e.g. to only add the files in the current directory non-recursively, do

git add $(find . -type f -maxdepth 1)

Alternatively, you could use

git ls-files --others --directory > file-list

to create a list of untracked files in your current directory, and edit it in an editor to remove everything you don't want to add. (Make sure to remove file-list itself.) You can then use

git add $(cat file-list)

to add the files and directories in the edited list. (Directories you leave in will still be added recursively).

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 1
    Good answer. Should be `git add $(find . -type f -maxdepth 1)` (missing . for path in find) – Ben Jun 08 '16 at 02:07
  • 1
    @Ben The dot is only necessary if you are using Apple's weird version of `find`. On Linux, we have GNU find and don't need the dot. – Sven Marnach Jun 08 '16 at 10:06
  • Ah, sorry for being weird :) worth mentioning though, there's a few of "us" – Ben Jun 08 '16 at 12:00
  • 1
    @Ben Yes, I added the dot since that way it should work on all platforms (at least at all platforms able to run git). – Sven Marnach Jun 08 '16 at 15:36
  • Note that .gitignore works when it is encountered when adding files/directories. So adding .\level1\level2 where level1 is full of files/directories can be achieved by a .gitignore in level1 like "!level2\n*" will exclude all of level1 except level2. – ahnkle Jul 19 '16 at 14:03
  • 1
    In contrary, Sven Marnach, adding a complex directory hierarchy is a quite usual thing: You experiment with a small piece of code and then you build a git repository around it. It then helps if you have perfect control about what to commit and what to ignore. A non-recursive approach is crucial in this case as no one wants to commit temporary files or binaries by accident. – Regis May Feb 11 '18 at 15:49
  • @RegisMay The idea is that you should commit often. You make a smal change, then you commit. Building a complex repository around some small piece of code doesn't seem to be a small atomic step. Anyway, people use git in many different ways, and that's fair enough, and this answer explains how to do what the OP asked for. :) – Sven Marnach Feb 11 '18 at 19:57
  • That changes nothing: There ARE scenarios where you require a detailed control about what to check in and what to ignore. Building a repository from an existing experiment is such a scenario. Even if it's just 10 or 20 files you deal with such control about what to check in and what to ignore is desirable. This IS an atomic step: The first in a larger series of future atomic steps. – Regis May Feb 13 '18 at 10:29
  • And yes, the answer explains that. Or better: This thread explains quite enough details about that. I was very happy that this question exists. – Regis May Feb 13 '18 at 10:41
  • 1
    @RegisMay Well, I just said it's rather uncommon, which I believe it is. – Sven Marnach Feb 13 '18 at 15:51
  • 1
    @Sven Marnach: Well, I just said, this is rather quite common, which I believe it is. – Regis May Feb 15 '18 at 19:24
  • @RegisMay I am currently migrating from TFS. “Adding a whole complex directory hierarchy” is a very common use case at the beginning of using git. – Martin Apr 09 '18 at 09:41
  • @Martin If you are importing the whole directory hierarchy into a single new git commit, you are doing it wrong. Google for "migrate from tfs to git" for plenty of better ways, including https://github.com/git-tfs/git-tfs. – Sven Marnach Apr 09 '18 at 10:10
  • @RegisMay Thanks for the hint. But we are not planing to use TFS any longer so git-tfs is not the solution. Do note that Microsoft suggest not to migrate the whole history and to manually migrate if you have large files: https://www.visualstudio.com/learn/migrate-from-tfvc-to-git/ As such the plan is to use the migration to get rid of old burdens. But thanks for the pointer. I did check it out. Might help those with simpler repositories who want to make a full 1:1 migrations. – Martin Apr 09 '18 at 11:13
  • `find` solution is pretty nice (although this version doesn't work for filenames with spaces). The other problem is that, although it probably solves OP's problem, it doesn't solve the question itself, since it wouldn't include removals. However, your great `ls-files` mention is possibly the solution I need (add THIS dir and no other subdirs) I'm currently playing with. Will post an answer if I get to something. – Charles Roberto Canato Jul 20 '19 at 17:52
6

If you want to add only the files from the directory without any other subfolders you can do something like:

git add FolderName/\*.* 

Where *.* means every file, from every file type. Folders don't have extensions so they won't pass.

Wald
  • 1,063
  • 7
  • 13
  • 3
    What if folders do have extensions and files are missing extensions? – skyking Sep 02 '15 at 07:02
  • @skyking You could use zshell wich has glop patters like `*(/)` and `*(.)` which matches either only directories or only files. But I am not sure it that is all that helpful. – Martin Apr 09 '18 at 09:44
2

A simple alternative is to add a single file in the subdirectory, then you can proceed to add other files as desired.

git add somedir/goodfile
cd somedir
git add anotherfile
  • OP wanted an add-all-the-plain-files option, not instructions on adding specific files one at a time. – jthill Apr 13 '20 at 00:53
  • The reason for this approach is because you can't add directories. My use case for only wanting directories is that sometimes I need the dirs as place-holders ready for the addition of files in a later commit or by another dev. – HankCa Apr 06 '21 at 00:15
0

I've reached this question, looking for its simple title answer: I wanted git to do all (additions and removals) of its add work, but only for the current dir and none of its subdirectories. Although @Sven Marnach's answer comes close with find, it would still not consider any removals. But he brilliantly mentioned git ls-files, which helped me do the job:

git ls-files -domk | grep -v / | uniq | xargs -I'{' git add '{'

I still find this a bit fragile as it can't handle files with a slash in its name. I think these are a bit more rare, so I guess this will work on most of the cases. But it does handle additions and removals nicely. The other restriction is it'll only work if you're currently in the directory from which you want to stage changes, since I use a slash as the hint to remove subdirectory entries from the listing to add.

Also, if you'll use this a lot, create an alias or a git command.