static enum path_treatment treat_one_path(struct dir_struct *dir, char *path, int *len, const struct path_simplify *simplify, int dtype, struct dirent *de) { int exclude = excluded(dir, path, &dtype); if (exclude && (dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path, *len, simplify)) dir_add_ignored(dir, path, *len); /* * Excluded? If we don't explicitly want to show * ignored files, ignore it */ if (exclude && !(dir->flags & DIR_SHOW_IGNORED)) return path_ignored; if (dtype == DT_UNKNOWN) dtype = get_dtype(de, path, *len); /* * Do we want to see just the ignored files? * We still need to recurse into directories, * even if we don't ignore them, since the * directory may contain files that we do.. */ if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) { if (dtype != DT_DIR) return path_ignored; } switch (dtype) { default: return path_ignored; case DT_DIR: memcpy(path + *len, "/", 2); (*len)++; switch (treat_directory(dir, path, *len, simplify)) { case show_directory: if (exclude != !!(dir->flags & DIR_SHOW_IGNORED)) return path_ignored; break; case recurse_into_directory: return path_recurse; case ignore_directory: return path_ignored; } break; case DT_REG: case DT_LNK: break; } return path_handled; }
int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; struct lock_file lock_file = LOCK_INIT; git_config(add_config, NULL); argc = parse_options(argc, argv, prefix, builtin_add_options, builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (0 <= addremove_explicit) addremove = addremove_explicit; else if (take_worktree_changes && ADDREMOVE_DEFAULT) addremove = 0; /* "-u" was given but not "-A" */ if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); if (!take_worktree_changes && addremove_explicit < 0 && argc) /* Turn "git add pathspec..." to "git add -A pathspec..." */ addremove = 1; if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') || chmod_arg[1] != 'x' || chmod_arg[2])) die(_("--chmod param '%s' must be either -x or +x"), chmod_arg); add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize; require_pathspec = !(take_worktree_changes || (0 < addremove_explicit)); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ parse_pathspec(&pathspec, PATHSPEC_ATTR, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); die_in_unpopulated_submodule(&the_index, prefix); die_path_inside_submodule(&the_index, &pathspec); if (add_new_files) { int baselen; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, &the_index, &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } if (pathspec.nr) { int i; if (!seen) seen = find_pathspecs_matching_against_index(&pathspec, &the_index); /* * file_exists() assumes exact match */ GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE | PATHSPEC_EXCLUDE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; if (pathspec.items[i].magic & PATHSPEC_EXCLUDE) continue; if (!seen[i] && path[0] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, &the_index, path, &dtype)) dir_add_ignored(&dir, &the_index, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), pathspec.items[i].original); } } free(seen); } plug_bulk_checkin(); if (add_renormalize) exit_status |= renormalize_tracked_files(&pathspec, flags); else exit_status |= add_files_to_cache(prefix, &pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); if (chmod_arg && pathspec.nr) chmod_pathspec(&pathspec, chmod_arg[0]); unplug_bulk_checkin(); finish: if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); UNLEAK(pathspec); UNLEAK(dir); return exit_status; }
int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; int newfd; const char **pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; git_config(add_config, NULL); argc = parse_options(argc, argv, prefix, builtin_add_options, builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) exit(interactive_add(argc - 1, argv + 1, prefix)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (addremove && take_worktree_changes) die("-A and -u are mutually incompatible"); if (!show_only && ignore_missing) die("Option --ignore-missing can only be used together with --dry-run"); if ((addremove || take_worktree_changes) && !argc) { static const char *here[2] = { ".", NULL }; argc = 1; argv = here; } add_new_files = !take_worktree_changes && !refresh_only; require_pathspec = !take_worktree_changes; newfd = hold_locked_index(&lock_file, 1); flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, "Nothing specified, nothing added.\n"); fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); return 0; } pathspec = validate_pathspec(argc, argv, prefix); if (read_cache() < 0) die("index file corrupt"); treat_gitlinks(pathspec); if (add_new_files) { int baselen; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, pathspec); if (pathspec) seen = prune_directory(&dir, pathspec, baselen); } if (refresh_only) { refresh(verbose, pathspec); goto finish; } if (pathspec) { int i; if (!seen) seen = find_used_pathspec(pathspec); for (i = 0; pathspec[i]; i++) { if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i])) { if (ignore_missing) { if (excluded(&dir, pathspec[i], DT_UNKNOWN)) dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); } else die("pathspec '%s' did not match any files", pathspec[i]); } } free(seen); } exit_status |= add_files_to_cache(prefix, pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die("Unable to write new index file"); } return exit_status; }
/* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git * doesn't handle them at all yet. Maybe that will change some * day. * * Also, we ignore the name ".git" (even if it is not a directory). * That likely will not change. * * Returns the most significant path_treatment value encountered in the scan. */ static enum path_treatment read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify) { DIR *fdir; enum path_treatment state, subdir_state, dir_state = path_none; struct dirent *de; struct strbuf path = STRBUF_INIT; strbuf_add(&path, base, baselen); fdir = opendir(path.len ? path.buf : "."); if (!fdir) goto out; while ((de = readdir(fdir)) != NULL) { /* check how the file or directory should be treated */ state = treat_path(dir, de, &path, baselen, simplify); if (state > dir_state) dir_state = state; /* recurse into subdir if instructed by treat_path */ if (state == path_recurse) { subdir_state = read_directory_recursive(dir, path.buf, path.len, check_only, simplify); if (subdir_state > dir_state) dir_state = subdir_state; } if (check_only) { /* abort early if maximum state has been reached */ if (dir_state == path_untracked) break; /* skip the dir_add_* part */ continue; } /* add the path to the appropriate result list */ switch (state) { case path_excluded: if (dir->flags & DIR_SHOW_IGNORED) dir_add_name(dir, path.buf, path.len); else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || ((dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path.buf, path.len, simplify))) dir_add_ignored(dir, path.buf, path.len); break; case path_untracked: if (!(dir->flags & DIR_SHOW_IGNORED)) dir_add_name(dir, path.buf, path.len); break; default: break; } } closedir(fdir); out: strbuf_release(&path); return dir_state; }
int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; int newfd; struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; int implicit_dot = 0; struct update_callback_data update_data; git_config(add_config, NULL); argc = parse_options(argc, argv, prefix, builtin_add_options, builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (0 <= addremove_explicit) addremove = addremove_explicit; else if (take_worktree_changes && ADDREMOVE_DEFAULT) addremove = 0; /* "-u" was given but not "-A" */ if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); /* * Warn when "git add pathspec..." was given without "-u" or "-A" * and pathspec... covers a removed path. */ memset(&update_data, 0, sizeof(update_data)); if (!take_worktree_changes && addremove_explicit < 0) update_data.warn_add_would_remove = 1; if (!take_worktree_changes && addremove_explicit < 0 && argc) /* * Turn "git add pathspec..." to "git add -A pathspec..." * in Git 2.0 but not yet */ ; /* addremove = 1; */ if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); if (addremove) { option_with_implicit_dot = "--all"; short_option_with_implicit_dot = "-A"; } if (take_worktree_changes) { option_with_implicit_dot = "--update"; short_option_with_implicit_dot = "-u"; } if (option_with_implicit_dot && !argc) { static const char *here[2] = { ".", NULL }; argc = 1; argv = here; implicit_dot = 1; } add_new_files = !take_worktree_changes && !refresh_only; require_pathspec = !take_worktree_changes; newfd = hold_locked_index(&lock_file, 1); flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)) | (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } if (read_cache() < 0) die(_("index file corrupt")); /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH | PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, prefix, argv); if (add_new_files) { int baselen; struct pathspec empty_pathspec; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen, implicit_dot ? WARN_IMPLICIT_DOT : 0); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } if (implicit_dot && prefix) refresh_cache(REFRESH_QUIET); if (pathspec.nr) { int i; if (!seen) seen = find_pathspecs_matching_against_index(&pathspec); /* * file_exists() assumes exact match */ GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; if (!seen[i] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, path, &dtype)) dir_add_ignored(&dir, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), pathspec.items[i].original); } } free(seen); } plug_bulk_checkin(); if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) { /* * Check for modified files throughout the worktree so * update_callback has a chance to warn about changes * outside the cwd. */ update_data.implicit_dot = prefix; update_data.implicit_dot_len = strlen(prefix); free_pathspec(&pathspec); memset(&pathspec, 0, sizeof(pathspec)); } update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; update_files_in_cache(prefix, &pathspec, &update_data); exit_status |= !!update_data.add_errors; if (add_new_files) exit_status |= add_files(&dir, flags); unplug_bulk_checkin(); finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return exit_status; }
/* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git * doesn't handle them at all yet. Maybe that will change some * day. * * Also, we ignore the name ".git" (even if it is not a directory). * That likely will not change. */ static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify) { DIR *fdir = opendir(path); int contents = 0; if (fdir) { struct dirent *de; char fullname[PATH_MAX + 1]; memcpy(fullname, base, baselen); while ((de = readdir(fdir)) != NULL) { int len, dtype; int exclude; if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git")) continue; len = strlen(de->d_name); /* Ignore overly long pathnames! */ if (len + baselen + 8 > sizeof(fullname)) continue; memcpy(fullname + baselen, de->d_name, len+1); if (simplify_away(fullname, baselen + len, simplify)) continue; dtype = DTYPE(de); exclude = excluded(dir, fullname, &dtype); if (exclude && (dir->flags & DIR_COLLECT_IGNORED) && in_pathspec(fullname, baselen + len, simplify)) dir_add_ignored(dir, fullname, baselen + len); /* * Excluded? If we don't explicitly want to show * ignored files, ignore it */ if (exclude && !(dir->flags & DIR_SHOW_IGNORED)) continue; if (dtype == DT_UNKNOWN) dtype = get_dtype(de, fullname); /* * Do we want to see just the ignored files? * We still need to recurse into directories, * even if we don't ignore them, since the * directory may contain files that we do.. */ if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) { if (dtype != DT_DIR) continue; } switch (dtype) { default: continue; case DT_DIR: memcpy(fullname + baselen + len, "/", 2); len++; switch (treat_directory(dir, fullname, baselen + len, simplify)) { case show_directory: if (exclude != !!(dir->flags & DIR_SHOW_IGNORED)) continue; break; case recurse_into_directory: contents += read_directory_recursive(dir, fullname, fullname, baselen + len, 0, simplify); continue; case ignore_directory: continue; } break; case DT_REG: case DT_LNK: break; } contents++; if (check_only) goto exit_early; else dir_add_name(dir, fullname, baselen + len); } exit_early: closedir(fdir); } return contents; }