/* * `untracked_files` will be filled with the names of untracked files. * The return value is: * * = 0 if there are not any untracked files * > 0 if there are untracked files */ static int get_untracked_files(struct pathspec ps, int include_untracked, struct strbuf *untracked_files) { int i; int max_len; int found = 0; char *seen; struct dir_struct dir; memset(&dir, 0, sizeof(dir)); if (include_untracked != INCLUDE_ALL_FILES) setup_standard_excludes(&dir); seen = xcalloc(ps.nr, 1); max_len = fill_directory(&dir, the_repository->index, &ps); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (dir_path_match(&the_index, ent, &ps, max_len, seen)) { found++; strbuf_addstr(untracked_files, ent->name); /* NUL-terminate: will be fed to update-index -z */ strbuf_addch(untracked_files, 0); } free(ent); } free(seen); free(dir.entries); free(dir.ignored); clear_directory(&dir); return found; }
static void wt_status_print_untracked(struct wt_status *s) { struct dir_struct dir; int i; int shown_header = 0; struct strbuf buf = STRBUF_INIT; memset(&dir, 0, sizeof(dir)); if (!s->untracked) { dir.show_other_directories = 1; dir.hide_empty_directories = 1; } setup_standard_excludes(&dir); read_directory(&dir, ".", "", 0, NULL); for(i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (!cache_name_is_other(ent->name, ent->len)) continue; if (!shown_header) { s->workdir_untracked = 1; wt_status_print_untracked_header(s); shown_header = 1; } color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s", quote_path(ent->name, ent->len, &buf, s->prefix)); } strbuf_release(&buf); }
static void fill_directory(struct dir_struct *dir, const char **pathspec, int ignored_too) { const char *path, *base; int baselen; /* Set up the default git porcelain excludes */ memset(dir, 0, sizeof(*dir)); if (!ignored_too) { dir->collect_ignored = 1; setup_standard_excludes(dir); } /* * Calculate common prefix for the pathspec, and * use that to optimize the directory walk */ baselen = common_prefix(pathspec); path = "."; base = ""; if (baselen) path = base = xmemdupz(*pathspec, baselen); /* Read the directory and prune it */ read_directory(dir, path, base, baselen, pathspec); if (pathspec) prune_directory(dir, pathspec, baselen); }
static int option_parse_exclude_standard(const struct option *opt, const char *arg, int unset) { struct dir_struct *dir = opt->value; exc_given = 1; setup_standard_excludes(dir); return 0; }
static void wt_status_collect_untracked(struct wt_status *s) { int i; struct dir_struct dir; struct timeval t_begin; if (!s->show_untracked_files) return; if (advice_status_u_option) gettimeofday(&t_begin, NULL); memset(&dir, 0, sizeof(dir)); if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; setup_standard_excludes(&dir); fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (cache_name_is_other(ent->name, ent->len) && match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) string_list_insert(&s->untracked, ent->name); free(ent); } if (s->show_ignored_files) { dir.nr = 0; dir.flags = DIR_SHOW_IGNORED; if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (cache_name_is_other(ent->name, ent->len) && match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) string_list_insert(&s->ignored, ent->name); free(ent); } } free(dir.entries); if (advice_status_u_option) { struct timeval t_end; gettimeofday(&t_end, NULL); s->untracked_in_ms = (uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 - ((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000); } }
int checkout_fast_forward(const unsigned char *head, const unsigned char *remote, int overwrite_ignore) { struct tree *trees[MAX_UNPACK_TREES]; struct unpack_trees_options opts; struct tree_desc t[MAX_UNPACK_TREES]; int i, nr_trees = 0; struct dir_struct dir; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); refresh_cache(REFRESH_QUIET); hold_locked_index(lock_file, 1); memset(&trees, 0, sizeof(trees)); memset(&opts, 0, sizeof(opts)); memset(&t, 0, sizeof(t)); if (overwrite_ignore) { memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; setup_standard_excludes(&dir); opts.dir = &dir; } opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.update = 1; opts.verbose_update = 1; opts.merge = 1; opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) return -1; trees[nr_trees] = parse_tree_indirect(remote); if (!trees[nr_trees++]) return -1; for (i = 0; i < nr_trees; i++) { parse_tree(trees[i]); init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); } if (unpack_trees(nr_trees, t, &opts)) return -1; if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); return 0; }
static int grep_directory(struct grep_opt *opt, const char **paths) { struct dir_struct dir; int i, hit = 0; memset(&dir, 0, sizeof(dir)); setup_standard_excludes(&dir); fill_directory(&dir, paths); for (i = 0; i < dir.nr; i++) { hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) break; } return hit; }
static int check_ignore(const char *prefix, const char **pathspec) { struct dir_struct dir; const char *path, *full_path; char *seen; int num_ignored = 0, dtype = DT_UNKNOWN, i; struct exclude *exclude; /* read_cache() is only necessary so we can watch out for submodules. */ if (read_cache() < 0) die(_("index file corrupt")); memset(&dir, 0, sizeof(dir)); setup_standard_excludes(&dir); if (!pathspec || !*pathspec) { if (!quiet) fprintf(stderr, "no pathspec given.\n"); return 0; } /* * look for pathspecs matching entries in the index, since these * should not be ignored, in order to be consistent with * 'git status', 'git add' etc. */ seen = find_pathspecs_matching_against_index(pathspec); for (i = 0; pathspec[i]; i++) { path = pathspec[i]; full_path = prefix_path(prefix, prefix ? strlen(prefix) : 0, path); full_path = check_path_for_gitlink(full_path); die_if_path_beyond_symlink(full_path, prefix); if (!seen[i]) { exclude = last_exclude_matching(&dir, full_path, &dtype); if (exclude) { if (!quiet) output_exclude(path, exclude); num_ignored++; } } } free(seen); clear_directory(&dir); return num_ignored; }
static void wt_status_collect_untracked(struct wt_status *s) { int i; struct dir_struct dir; uint64_t t_begin = getnanotime(); if (!s->show_untracked_files) return; memset(&dir, 0, sizeof(dir)); if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; if (s->show_ignored_files) dir.flags |= DIR_SHOW_IGNORED_TOO; else dir.untracked = the_index.untracked; setup_standard_excludes(&dir); fill_directory(&dir, &s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (cache_name_is_other(ent->name, ent->len) && dir_path_match(ent, &s->pathspec, 0, NULL)) string_list_insert(&s->untracked, ent->name); free(ent); } for (i = 0; i < dir.ignored_nr; i++) { struct dir_entry *ent = dir.ignored[i]; if (cache_name_is_other(ent->name, ent->len) && dir_path_match(ent, &s->pathspec, 0, NULL)) string_list_insert(&s->ignored, ent->name); free(ent); } free(dir.entries); free(dir.ignored); clear_directory(&dir); if (advice_status_u_option) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; }
static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, int exc_std) { struct dir_struct dir; int i, hit = 0; memset(&dir, 0, sizeof(dir)); if (exc_std) setup_standard_excludes(&dir); fill_directory(&dir, pathspec); for (i = 0; i < dir.nr; i++) { if (!dir_path_match(dir.entries[i], pathspec, 0, NULL)) continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) break; } return hit; }
static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec) { struct dir_struct dir; int i, hit = 0; memset(&dir, 0, sizeof(dir)); setup_standard_excludes(&dir); fill_directory(&dir, pathspec->raw); for (i = 0; i < dir.nr; i++) { const char *name = dir.entries[i]->name; int namelen = strlen(name); if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL)) continue; hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) break; } return hit; }
static void wt_status_collect_untracked(struct wt_status *s) { int i; struct dir_struct dir; if (!s->show_untracked_files) return; memset(&dir, 0, sizeof(dir)); if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; setup_standard_excludes(&dir); fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (!cache_name_is_other(ent->name, ent->len)) continue; if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) continue; string_list_insert(&s->untracked, ent->name); free(ent); } if (s->show_ignored_files) { dir.nr = 0; dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES; fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (!cache_name_is_other(ent->name, ent->len)) continue; if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) continue; string_list_insert(&s->ignored, ent->name); free(ent); } } free(dir.entries); }
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; }
int cmd_clean(int argc, const char **argv, const char *prefix) { int i; int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; int ignored_only = 0, baselen = 0, config_set = 0, errors = 0; int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf directory = STRBUF_INIT; struct dir_struct dir; static const char **pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; const char *qname; char *seen = NULL; struct option options[] = { OPT__QUIET(&quiet), OPT__DRY_RUN(&show_only), OPT_BOOLEAN('f', "force", &force, "force"), OPT_BOOLEAN('d', NULL, &remove_directories, "remove whole directories"), { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern", "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb }, OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), OPT_BOOLEAN('X', NULL, &ignored_only, "remove only ignored files"), OPT_END() }; git_config(git_clean_config, NULL); if (force < 0) force = 0; else config_set = 1; argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, 0); memset(&dir, 0, sizeof(dir)); if (ignored_only) dir.flags |= DIR_SHOW_IGNORED; if (ignored && ignored_only) die("-x and -X cannot be used together"); if (!show_only && !force) die("clean.requireForce %s to true and neither -n nor -f given; " "refusing to clean", config_set ? "set" : "defaults"); if (force > 1) rm_flags = 0; dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (read_cache() < 0) die("index file corrupt"); if (!ignored) setup_standard_excludes(&dir); for (i = 0; i < exclude_list.nr; i++) add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list); pathspec = get_pathspec(prefix, argv); fill_directory(&dir, pathspec); if (pathspec) seen = xmalloc(argc > 0 ? argc : 1); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; int len, pos; int matches = 0; struct cache_entry *ce; struct stat st; /* * Remove the '/' at the end that directory * walking adds for directory entries. */ len = ent->len; if (len && ent->name[len-1] == '/') len--; pos = cache_name_pos(ent->name, len); if (0 <= pos) continue; /* exact match */ pos = -pos - 1; if (pos < active_nr) { ce = active_cache[pos]; if (ce_namelen(ce) == len && !memcmp(ce->name, ent->name, len)) continue; /* Yup, this one exists unmerged */ } /* * we might have removed this as part of earlier * recursive directory removal, so lstat() here could * fail with ENOENT. */ if (lstat(ent->name, &st)) continue; if (pathspec) { memset(seen, 0, argc > 0 ? argc : 1); matches = match_pathspec(pathspec, ent->name, len, baselen, seen); } if (S_ISDIR(st.st_mode)) { strbuf_addstr(&directory, ent->name); qname = quote_path_relative(directory.buf, directory.len, &buf, prefix); if (show_only && (remove_directories || (matches == MATCHED_EXACTLY))) { printf("Would remove %s\n", qname); } else if (remove_directories || (matches == MATCHED_EXACTLY)) { if (!quiet) printf("Removing %s\n", qname); if (remove_dir_recursively(&directory, rm_flags) != 0) { warning("failed to remove '%s'", qname); errors++; } } else if (show_only) { printf("Would not remove %s\n", qname); } else { printf("Not removing %s\n", qname); } strbuf_reset(&directory); } else { if (pathspec && !matches) continue; qname = quote_path_relative(ent->name, -1, &buf, prefix); if (show_only) { printf("Would remove %s\n", qname); continue; } else if (!quiet) { printf("Removing %s\n", qname); } if (unlink(ent->name) != 0) { warning("failed to remove '%s'", qname); errors++; } } } free(seen); strbuf_release(&directory); string_list_clear(&exclude_list, 0); return (errors != 0); }
int checkout_fast_forward(const struct object_id *head, const struct object_id *remote, int overwrite_ignore) { struct tree *trees[MAX_UNPACK_TREES]; struct unpack_trees_options opts; struct tree_desc t[MAX_UNPACK_TREES]; int i, nr_trees = 0; struct dir_struct dir; struct lock_file lock_file = LOCK_INIT; refresh_cache(REFRESH_QUIET); if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0) return -1; memset(&trees, 0, sizeof(trees)); memset(&t, 0, sizeof(t)); trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) { rollback_lock_file(&lock_file); return -1; } trees[nr_trees] = parse_tree_indirect(remote); if (!trees[nr_trees++]) { rollback_lock_file(&lock_file); return -1; } for (i = 0; i < nr_trees; i++) { parse_tree(trees[i]); init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); } memset(&opts, 0, sizeof(opts)); if (overwrite_ignore) { memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; setup_standard_excludes(&dir); opts.dir = &dir; } opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.update = 1; opts.verbose_update = 1; opts.merge = 1; opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); if (unpack_trees(nr_trees, t, &opts)) { rollback_lock_file(&lock_file); clear_unpack_trees_porcelain(&opts); return -1; } clear_unpack_trees_porcelain(&opts); if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) return error(_("unable to write new index file")); return 0; }
static int merge_working_tree(const struct checkout_opts *opts, struct branch_info *old_branch_info, struct branch_info *new_branch_info, int *writeout_error) { int ret; struct lock_file lock_file = LOCK_INIT; hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache_preload(NULL) < 0) return error(_("index file corrupt")); resolve_undo_clear(); if (opts->force) { ret = reset_tree(get_commit_tree(new_branch_info->commit), opts, 1, writeout_error); if (ret) return ret; } else { struct tree_desc trees[2]; struct tree *tree; struct unpack_trees_options topts; memset(&topts, 0, sizeof(topts)); topts.head_idx = -1; topts.src_index = &the_index; topts.dst_index = &the_index; setup_unpack_trees_porcelain(&topts, "checkout"); refresh_cache(REFRESH_QUIET); if (unmerged_cache()) { error(_("you need to resolve your current index first")); return 1; } /* 2-way merge to the new branch */ topts.initial_checkout = is_cache_unborn(); topts.update = 1; topts.merge = 1; topts.gently = opts->merge && old_branch_info->commit; topts.verbose_update = opts->show_progress; topts.fn = twoway_merge; if (opts->overwrite_ignore) { topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir->flags |= DIR_SHOW_IGNORED; setup_standard_excludes(topts.dir); } tree = parse_tree_indirect(old_branch_info->commit ? &old_branch_info->commit->object.oid : the_hash_algo->empty_tree); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(&new_branch_info->commit->object.oid); init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); clear_unpack_trees_porcelain(&topts); if (ret == -1) { /* * Unpack couldn't do a trivial merge; either * give up or do a real merge, depending on * whether the merge flag was used. */ struct tree *result; struct tree *work; struct merge_options o; if (!opts->merge) return 1; /* * Without old_branch_info->commit, the below is the same as * the two-tree unpack we already tried and failed. */ if (!old_branch_info->commit) return 1; /* Do more real merge */ /* * We update the index fully, then write the * tree from the index, then merge the new * branch with the current tree, with the old * branch as the base. Then we reset the index * (but not the working tree) to the new * branch, leaving the working tree as the * merged version, but skipping unmerged * entries in the index. */ add_files_to_cache(NULL, NULL, 0); /* * NEEDSWORK: carrying over local changes * when branches have different end-of-line * normalization (or clean+smudge rules) is * a pain; plumb in an option to set * o.renormalize? */ init_merge_options(&o, the_repository); o.verbosity = 0; work = write_tree_from_memory(&o); ret = reset_tree(get_commit_tree(new_branch_info->commit), opts, 1, writeout_error); if (ret) return ret; o.ancestor = old_branch_info->name; o.branch1 = new_branch_info->name; o.branch2 = "local"; ret = merge_trees(&o, get_commit_tree(new_branch_info->commit), work, get_commit_tree(old_branch_info->commit), &result); if (ret < 0) exit(128); ret = reset_tree(get_commit_tree(new_branch_info->commit), opts, 0, writeout_error); strbuf_release(&o.obuf); if (ret) return ret; } } if (!active_cache_tree) active_cache_tree = cache_tree(); if (!cache_tree_fully_valid(active_cache_tree)) cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); if (!opts->force && !opts->quiet) show_local_changes(&new_branch_info->commit->object, &opts->diff_options); return 0; }
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; }
int cmd_ls_files(int argc, const char **argv, const char *prefix) { int i; int exc_given = 0, require_work_tree = 0; struct dir_struct dir; memset(&dir, 0, sizeof(dir)); if (prefix) prefix_offset = strlen(prefix); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) { i++; break; } if (!strcmp(arg, "-z")) { line_terminator = 0; continue; } if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) { tag_cached = "H "; tag_unmerged = "M "; tag_removed = "R "; tag_modified = "C "; tag_other = "? "; tag_killed = "K "; if (arg[1] == 'v') show_valid_bit = 1; continue; } if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) { show_cached = 1; continue; } if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { show_deleted = 1; continue; } if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { show_modified = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) { show_others = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { dir.show_ignored = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { show_stage = 1; continue; } if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) { show_killed = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "--directory")) { dir.show_other_directories = 1; continue; } if (!strcmp(arg, "--no-empty-directory")) { dir.hide_empty_directories = 1; continue; } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { /* There's no point in showing unmerged unless * you also show the stage information. */ show_stage = 1; show_unmerged = 1; continue; } if (!strcmp(arg, "-x") && i+1 < argc) { exc_given = 1; add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!prefixcmp(arg, "--exclude=")) { exc_given = 1; add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!strcmp(arg, "-X") && i+1 < argc) { exc_given = 1; add_excludes_from_file(&dir, argv[++i]); continue; } if (!prefixcmp(arg, "--exclude-from=")) { exc_given = 1; add_excludes_from_file(&dir, arg+15); continue; } if (!prefixcmp(arg, "--exclude-per-directory=")) { exc_given = 1; dir.exclude_per_dir = arg + 24; continue; } if (!strcmp(arg, "--exclude-standard")) { exc_given = 1; setup_standard_excludes(&dir); continue; } if (!strcmp(arg, "--full-name")) { prefix_offset = 0; continue; } if (!strcmp(arg, "--error-unmatch")) { error_unmatch = 1; continue; } if (!prefixcmp(arg, "--with-tree=")) { with_tree = arg + 12; continue; } if (!prefixcmp(arg, "--abbrev=")) { abbrev = strtoul(arg+9, NULL, 10); if (abbrev && abbrev < MINIMUM_ABBREV) abbrev = MINIMUM_ABBREV; else if (abbrev > 40) abbrev = 40; continue; } if (!strcmp(arg, "--abbrev")) { abbrev = DEFAULT_ABBREV; continue; } if (*arg == '-') usage(ls_files_usage); break; } if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); pathspec = get_pathspec(prefix, argv + i); /* Verify that the pathspec matches the prefix */ if (pathspec) prefix = verify_pathspec(prefix); /* Treat unmatching pathspec elements as errors */ if (pathspec && error_unmatch) { int num; for (num = 0; pathspec[num]; num++) ; ps_matched = xcalloc(1, num); } if (dir.show_ignored && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); exit(1); } /* With no flags, we default to showing the cached files */ if (!(show_stage | show_deleted | show_others | show_unmerged | show_killed | show_modified)) show_cached = 1; read_cache(); if (prefix) prune_cache(prefix); if (with_tree) { /* * Basic sanity check; show-stages and show-unmerged * would not make any sense with this option. */ if (show_stage || show_unmerged) die("ls-files --with-tree is incompatible with -s or -u"); overlay_tree_on_cache(with_tree, prefix); } show_files(&dir, prefix); if (ps_matched) { int bad; bad = report_path_error(ps_matched, pathspec, prefix_offset); if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); return bad ? 1 : 0; } return 0; }