/* * A simpler implementation of relative_path * * Get relative path by removing "prefix" from "in". This function * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter * to increase performance when traversing the path to work_tree. */ const char *remove_leading_path(const char *in, const char *prefix) { static char buf[PATH_MAX + 1]; int i = 0, j = 0; if (!prefix || !prefix[0]) return in; while (prefix[i]) { if (is_dir_sep(prefix[i])) { if (!is_dir_sep(in[j])) return in; while (is_dir_sep(prefix[i])) i++; while (is_dir_sep(in[j])) j++; continue; } else if (in[j] != prefix[i]) { return in; } i++; j++; } if ( /* "/foo" is a prefix of "/foo" */ in[j] && /* "/foo" is not a prefix of "/foobar" */ !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j]) ) return in; while (is_dir_sep(in[j])) j++; if (!in[j]) strcpy(buf, "."); else strcpy(buf, in + j); return buf; }
const char *relative_path(const char *abs, const char *base) { static char buf[PATH_MAX + 1]; int i = 0, j = 0; if (!base || !base[0]) return abs; while (base[i]) { if (is_dir_sep(base[i])) { if (!is_dir_sep(abs[j])) return abs; while (is_dir_sep(base[i])) i++; while (is_dir_sep(abs[j])) j++; continue; } else if (abs[j] != base[i]) { return abs; } i++; j++; } if ( /* "/foo" is a prefix of "/foo" */ abs[j] && /* "/foo" is not a prefix of "/foobar" */ !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j]) ) return abs; while (is_dir_sep(abs[j])) j++; if (!abs[j]) strcpy(buf, "."); else strcpy(buf, abs + j); return buf; }
/*=========================================== * filepath -- Find file in sequence of paths * handles NULL in either argument * returns alloc'd buffer * Warning: if called with mode other than "r" this may not * do what you want, because 1) if file is not found, it appends ext. * and 2) file always goes in 1st specified directory of path unless * name is absolute or ./something *=========================================*/ STRING filepath (CNSTRING name, CNSTRING mode, CNSTRING path, CNSTRING ext, INT utf8) { char buf1[MAXPATHLEN], buf2[MAXPATHLEN]; STRING p, q; INT nlen, elen; if (ISNULL(name)) return NULL; if (ISNULL(path)) return strsave(name); nlen = strlen(name); if (ext && *ext) { elen = strlen(ext); if ((nlen > elen) && path_match(name+nlen-elen, ext)) { ext = NULL; elen = 0; } } else { ext = NULL; elen = 0; } /* at this point ext is non-null only if name doesn't * end in ext. I.E. we need to check for name + ext and name */ if (nlen + strlen(path) + elen >= MAXLINELEN) return NULL; /* for absolute and relative path names we first check the * pathname for validity relative to the current directory */ if (is_path(name)) { /* If this is a path, i.e. it has multiple path elements * use the name as is first. So absolute paths don't * get resolved by appending search paths. */ if (ext) { strcpy(buf1,name); nlen = strlen(buf1); strcat(buf1, ext); if (access(buf1, 0) == 0) return strsave(buf1); buf1[nlen] = '\0'; /* remove extension */ if (access(buf1, 0) == 0) return strsave(buf1); } if (access(name, 0) == 0) return strsave(name); /* fail if get here and name begins with a / or ./ * as we didn't find the file. * however let foo/bar sneak thru, so we allow access * to files in subdirectories of the search path if * named in the filename. */ if (is_dir_sep(name[0]) || (name[0] == '.' && is_dir_sep(name[1]))) return strsave(name); #ifdef WIN32 if (name[0] && name[1]==':' && isasciiletter(name[0])) { return strsave(name); } #endif } /* it is a relative path, so search for it in search path */ strcpy(buf1, path); zero_separate_path(buf1); p = buf1; while (*p) { q = buf2; strcpy(q, p); expand_special_fname_chars(buf2, sizeof(buf2), utf8); q += strlen(q); if (q>buf2 && !is_dir_sep(q[-1])) { strcpy(q, LLSTRDIRSEPARATOR); q++; } strcpy(q, name); if (ext) { nlen = strlen(buf2); strcat(buf2, ext); if (access(buf2, 0) == 0) return strsave(buf2); buf2[nlen] = '\0'; /* remove extension */ } if (access(buf2, 0) == 0) return strsave(buf2); p += strlen(p); p++; } if (mode[0] == 'r') return NULL; p = buf1; q = buf2; strcpy(q, p); expand_special_fname_chars(buf2, sizeof(buf2), utf8); q += strlen(q); if (q>buf2 && !is_dir_sep(q[-1])) { strcpy(q, LLSTRDIRSEPARATOR); q++; } strcpy(q, name); if (ext) strcat(q, ext); return strsave(buf2); }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0; int seen_dashdash = 0; int external_grep_allowed__ignored; const char *show_in_pager = NULL, *default_pager = "dummy"; struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; const char **paths = NULL; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; int dummy; int use_index = 1; struct option options[] = { OPT_BOOLEAN(0, "cached", &cached, "search in index instead of in the work tree"), OPT_BOOLEAN(0, "index", &use_index, "--no-index finds in contents not managed by git"), OPT_GROUP(""), OPT_BOOLEAN('v', "invert-match", &opt.invert, "show non-matching lines"), OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, "case insensitive matching"), OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, "match patterns only at word boundaries"), OPT_SET_INT('a', "text", &opt.binary, "process binary files as text", GREP_BINARY_TEXT), OPT_SET_INT('I', NULL, &opt.binary, "don't match patterns in binary files", GREP_BINARY_NOMATCH), { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth", "descend at most <depth> levels", PARSE_OPT_NONEG, NULL, 1 }, OPT_GROUP(""), OPT_BIT('E', "extended-regexp", &opt.regflags, "use extended POSIX regular expressions", REG_EXTENDED), OPT_NEGBIT('G', "basic-regexp", &opt.regflags, "use basic POSIX regular expressions (default)", REG_EXTENDED), OPT_BOOLEAN('F', "fixed-strings", &opt.fixed, "interpret patterns as fixed strings"), OPT_GROUP(""), OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"), OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1), OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1), OPT_NEGBIT(0, "full-name", &opt.relative, "show filenames relative to top directory", 1), OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, "show only filenames instead of matching lines"), OPT_BOOLEAN(0, "name-only", &opt.name_only, "synonym for --files-with-matches"), OPT_BOOLEAN('L', "files-without-match", &opt.unmatch_name_only, "show only the names of files without match"), OPT_BOOLEAN('z', "null", &opt.null_following_name, "print NUL after filenames"), OPT_BOOLEAN('c', "count", &opt.count, "show the number of matches instead of matching lines"), OPT__COLOR(&opt.color, "highlight matches"), OPT_GROUP(""), OPT_CALLBACK('C', NULL, &opt, "n", "show <n> context lines before and after matches", context_callback), OPT_INTEGER('B', NULL, &opt.pre_context, "show <n> context lines before matches"), OPT_INTEGER('A', NULL, &opt.post_context, "show <n> context lines after matches"), OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM", context_callback), OPT_BOOLEAN('p', "show-function", &opt.funcname, "show a line with the function name before matches"), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, "file", "read patterns from file", file_callback), { OPTION_CALLBACK, 'e', NULL, &opt, "pattern", "match <pattern>", PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, "combine patterns specified with -e", PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOLEAN(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, open_callback }, { OPTION_CALLBACK, ')', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, close_callback }, OPT_BOOLEAN('q', "quiet", &opt.status_only, "indicate hit with exit status without output"), OPT_BOOLEAN(0, "all-match", &opt.all_match, "show only matches from files that match all patterns"), OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, "pager", "show matching files in the pager", PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored, "allow calling of grep(1) (ignored by this build)"), { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage", PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() }; /* * 'git grep -h', unlike 'git grep -h <pattern>', is a request * to show usage information and exit. */ if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(grep_usage, options); memset(&opt, 0, sizeof(opt)); opt.prefix = prefix; opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt.relative = 1; opt.pathname = 1; opt.pattern_tail = &opt.pattern_list; opt.header_tail = &opt.header_list; opt.regflags = REG_NEWLINE; opt.max_depth = -1; strcpy(opt.color_context, ""); strcpy(opt.color_filename, ""); strcpy(opt.color_function, ""); strcpy(opt.color_lineno, ""); strcpy(opt.color_match, GIT_COLOR_BOLD_RED); strcpy(opt.color_selected, ""); strcpy(opt.color_sep, GIT_COLOR_CYAN); opt.color = -1; git_config(grep_config, &opt); if (opt.color == -1) opt.color = git_use_color_default; /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or * -f, we take the first unrecognized non option to be the * pattern, but then what follows it must be zero or more * valid refs up to the -- (if exists), and then existing * paths. If there is an explicit pattern, then the first * unrecognized non option is the beginning of the refs list * that continues up to the -- (if exists), and then paths. */ argc = parse_options(argc, argv, prefix, options, grep_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION | PARSE_OPT_NO_INTERNAL_HELP); if (use_index && !startup_info->have_repository) /* die the same way as if we did it at the beginning */ setup_git_directory(); /* * skip a -- separator; we know it cannot be * separating revisions from pathnames if * we haven't even had any patterns yet */ if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) { argv++; argc--; } /* First unrecognized non-option token */ if (argc > 0 && !opt.pattern_list) { append_grep_pattern(&opt, argv[0], "command line", 0, GREP_PATTERN); argv++; argc--; } if (show_in_pager == default_pager) show_in_pager = git_pager(1); if (show_in_pager) { opt.color = 0; opt.name_only = 1; opt.null_following_name = 1; opt.output_priv = &path_list; opt.output = append_path; string_list_append(&path_list, show_in_pager); use_threads = 0; } if (!opt.pattern_list) die("no pattern given."); if (!opt.fixed && opt.ignore_case) opt.regflags |= REG_ICASE; if ((opt.regflags != REG_NEWLINE) && opt.fixed) die("cannot mix --fixed-strings and regexp"); #ifndef NO_PTHREADS if (online_cpus() == 1 || !grep_threads_ok(&opt)) use_threads = 0; if (use_threads) { if (opt.pre_context || opt.post_context) print_hunk_marks_between_files = 1; start_threads(&opt); } #else use_threads = 0; #endif compile_grep_patterns(&opt); /* Check revs and then paths */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) die("bad object %s", arg); add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } /* The rest are paths */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j]); } if (i < argc) paths = get_pathspec(prefix, argv + i); else if (prefix) { paths = xcalloc(2, sizeof(const char *)); paths[0] = prefix; paths[1] = NULL; } if (show_in_pager && (cached || list.nr)) die("--open-files-in-pager only works on the worktree"); if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) { const char *pager = path_list.items[0].string; int len = strlen(pager); if (len > 4 && is_dir_sep(pager[len - 5])) pager += len - 4; if (!strcmp("less", pager) || !strcmp("vi", pager)) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "+/%s%s", strcmp("less", pager) ? "" : "*", opt.pattern_list->pattern); string_list_append(&path_list, buf.buf); strbuf_detach(&buf, NULL); } } if (!show_in_pager) setup_pager(); if (!use_index) { if (cached) die("--cached cannot be used with --no-index."); if (list.nr) die("--no-index cannot be used with revs."); hit = grep_directory(&opt, paths); } else if (!list.nr) { if (!cached) setup_work_tree(); hit = grep_cache(&opt, paths, cached); } else { if (cached) die("both --cached and trees are given."); hit = grep_objects(&opt, paths, &list); } if (use_threads) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); free_grep_patterns(&opt); return !hit; }
static int dir_prefix(const char *buf, const char *dir) { int len = strlen(dir); return !strncmp(buf, dir, len) && (is_dir_sep(buf[len]) || buf[len] == '\0'); }
/* * It is okay if dst == src, but they should not overlap otherwise. * * Performs the following normalizations on src, storing the result in dst: * - Ensures that components are separated by '/' (Windows only) * - Squashes sequences of '/'. * - Removes "." components. * - Removes ".." components, and the components the precede them. * Returns failure (non-zero) if a ".." component appears as first path * component anytime during the normalization. Otherwise, returns success (0). * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. * * prefix_len != NULL is for a specific case of prefix_pathspec(): * assume that src == dst and src[0..prefix_len-1] is already * normalized, any time "../" eats up to the prefix_len part, * prefix_len is reduced. In the end prefix_len is the remaining * prefix that has not been overridden by user pathspec. * * NEEDSWORK: This function doesn't perform normalization w.r.t. trailing '/'. * For everything but the root folder itself, the normalized path should not * end with a '/', then the callers need to be fixed up accordingly. * */ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; int i = has_unc_prefix(src); for (i = i ? i : has_dos_drive_prefix(src); i > 0; i--) *dst++ = *src++; dst0 = dst; if (is_dir_sep(*src)) { *dst++ = '/'; while (is_dir_sep(*src)) src++; } for (;;) { char c = *src; /* * A path component that begins with . could be * special: * (1) "." and ends -- ignore and terminate. * (2) "./" -- ignore them, eat slash and continue. * (3) ".." and ends -- strip one and terminate. * (4) "../" -- strip one, eat slash and continue. */ if (c == '.') { if (!src[1]) { /* (1) */ src++; } else if (is_dir_sep(src[1])) { /* (2) */ src += 2; while (is_dir_sep(*src)) src++; continue; } else if (src[1] == '.') { if (!src[2]) { /* (3) */ src += 2; goto up_one; } else if (is_dir_sep(src[2])) { /* (4) */ src += 3; while (is_dir_sep(*src)) src++; goto up_one; } } } /* copy up to the next '/', and eat all '/' */ while ((c = *src++) != '\0' && !is_dir_sep(c)) *dst++ = c; if (is_dir_sep(c)) { *dst++ = '/'; while (is_dir_sep(c)) c = *src++; src--; } else if (!c) break; continue; up_one: /* * dst0..dst is prefix portion, and dst[-1] is '/'; * go up one level. */ dst--; /* go to trailing '/' */ if (dst <= dst0) return -1; /* Windows: dst[-1] cannot be backslash anymore */ while (dst0 < dst && dst[-1] != '/') dst--; if (prefix_len && *prefix_len > dst - dst0) *prefix_len = dst - dst0; } *dst = '\0'; return 0; }
static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start; char *dir; /* * Strip trailing spaces, slashes and /.git */ while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) end--; if (end - repo > 5 && is_dir_sep(end[-5]) && !strncmp(end - 4, ".git", 4)) { end -= 5; while (repo < end && is_dir_sep(end[-1])) end--; } /* * Find last component, but be prepared that repo could have * the form "remote.example.com:foo.git", i.e. no slash * in the directory part. */ start = end; while (repo < start && !is_dir_sep(start[-1]) && start[-1] != ':') start--; /* * Strip .{bundle,git}. */ if (is_bundle) { if (end - start > 7 && !strncmp(end - 7, ".bundle", 7)) end -= 7; } else { if (end - start > 4 && !strncmp(end - 4, ".git", 4)) end -= 4; } if (is_bare) { struct strbuf result = STRBUF_INIT; strbuf_addf(&result, "%.*s.git", (int)(end - start), start); dir = strbuf_detach(&result, NULL); } else dir = xstrndup(start, end - start); /* * Replace sequences of 'control' characters and whitespace * with one ascii space, remove leading and trailing spaces. */ if (*dir) { char *out = dir; int prev_space = 1 /* strip leading whitespace */; for (end = dir; *end; ++end) { char ch = *end; if ((unsigned char)ch < '\x20') ch = '\x20'; if (isspace(ch)) { if (prev_space) continue; prev_space = 1; } else prev_space = 0; *out++ = ch; } *out = '\0'; if (out > dir && prev_space) out[-1] = '\0'; } return dir; }
static void prepare_attr_stack(const char *path, int dirlen, struct attr_stack **stack) { struct attr_stack *info; struct strbuf pathbuf = STRBUF_INIT; /* * At the bottom of the attribute stack is the built-in * set of attribute definitions, followed by the contents * of $(prefix)/etc/gitattributes and a file specified by * core.attributesfile. Then, contents from * .gitattribute files from directories closer to the * root to the ones in deeper directories are pushed * to the stack. Finally, at the very top of the stack * we always keep the contents of $GIT_DIR/info/attributes. * * When checking, we use entries from near the top of the * stack, preferring $GIT_DIR/info/attributes, then * .gitattributes in deeper directories to shallower ones, * and finally use the built-in set as the default. */ bootstrap_attr_stack(stack); /* * Pop the "info" one that is always at the top of the stack. */ info = *stack; *stack = info->prev; /* * Pop the ones from directories that are not the prefix of * the path we are checking. Break out of the loop when we see * the root one (whose origin is an empty string "") or the builtin * one (whose origin is NULL) without popping it. */ while ((*stack)->origin) { int namelen = (*stack)->originlen; struct attr_stack *elem; elem = *stack; if (namelen <= dirlen && !strncmp(elem->origin, path, namelen) && (!namelen || path[namelen] == '/')) break; debug_pop(elem); *stack = elem->prev; attr_stack_free(elem); } /* * bootstrap_attr_stack() should have added, and the * above loop should have stopped before popping, the * root element whose attr_stack->origin is set to an * empty string. */ assert((*stack)->origin); strbuf_addstr(&pathbuf, (*stack)->origin); /* Build up to the directory 'path' is in */ while (pathbuf.len < dirlen) { size_t len = pathbuf.len; struct attr_stack *next; char *origin; /* Skip path-separator */ if (len < dirlen && is_dir_sep(path[len])) len++; /* Find the end of the next component */ while (len < dirlen && !is_dir_sep(path[len])) len++; if (pathbuf.len > 0) strbuf_addch(&pathbuf, '/'); strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len)); strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE); next = read_attr(pathbuf.buf, 0); /* reset the pathbuf to not include "/.gitattributes" */ strbuf_setlen(&pathbuf, len); origin = xstrdup(pathbuf.buf); push_stack(stack, next, origin, len); } /* * Finally push the "info" one at the top of the stack. */ push_stack(stack, info, NULL, 0); strbuf_release(&pathbuf); }
/* * Return the real path (i.e., absolute path, with symlinks resolved * and extra slashes removed) equivalent to the specified path. (If * you want an absolute path but don't mind links, use * absolute_path().) The return value is a pointer to a static * buffer. * * The input and all intermediate paths must be shorter than MAX_PATH. * The directory part of path (i.e., everything up to the last * dir_sep) must denote a valid, existing directory, but the last * component need not exist. If die_on_error is set, then die with an * informative error message if there is a problem. Otherwise, return * NULL on errors (without generating any output). * * If path is our buffer, then return path, as it's already what the * user wants. */ static const char *real_path_internal(const char *path, int die_on_error) { static struct strbuf sb = STRBUF_INIT; char *retval = NULL; /* * If we have to temporarily chdir(), store the original CWD * here so that we can chdir() back to it at the end of the * function: */ struct strbuf cwd = STRBUF_INIT; int depth = MAXDEPTH; char *last_elem = NULL; struct stat st; /* We've already done it */ if (path == sb.buf) return path; if (!*path) { if (die_on_error) die("The empty string is not a valid path"); else goto error_out; } strbuf_reset(&sb); strbuf_addstr(&sb, path); while (depth--) { if (!is_directory(sb.buf)) { char *last_slash = find_last_dir_sep(sb.buf); if (last_slash) { last_elem = xstrdup(last_slash + 1); strbuf_setlen(&sb, last_slash - sb.buf + 1); } else { last_elem = xmemdupz(sb.buf, sb.len); strbuf_reset(&sb); } } if (sb.len) { if (!cwd.len && strbuf_getcwd(&cwd)) { if (die_on_error) die_errno("Could not get current working directory"); else goto error_out; } if (chdir(sb.buf)) { if (die_on_error) die_errno("Could not switch to '%s'", sb.buf); else goto error_out; } } if (strbuf_getcwd(&sb)) { if (die_on_error) die_errno("Could not get current working directory"); else goto error_out; } if (last_elem) { if (sb.len && !is_dir_sep(sb.buf[sb.len - 1])) strbuf_addch(&sb, '/'); strbuf_addstr(&sb, last_elem); free(last_elem); last_elem = NULL; } if (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) { struct strbuf next_sb = STRBUF_INIT; ssize_t len = strbuf_readlink(&next_sb, sb.buf, 0); if (len < 0) { if (die_on_error) die_errno("Invalid symlink '%s'", sb.buf); else goto error_out; } strbuf_swap(&sb, &next_sb); strbuf_release(&next_sb); } else break; } retval = sb.buf; error_out: free(last_elem); if (cwd.len && chdir(cwd.buf)) die_errno("Could not change back to '%s'", cwd.buf); strbuf_release(&cwd); return retval; }
int dir_iterator_advance(struct dir_iterator *dir_iterator) { struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; while (1) { struct dir_iterator_level *level = &iter->levels[iter->levels_nr - 1]; struct dirent *de; if (!level->initialized) { /* * Note: dir_iterator_begin() ensures that * path is not the empty string. */ if (!is_dir_sep(iter->base.path.buf[iter->base.path.len - 1])) strbuf_addch(&iter->base.path, '/'); level->prefix_len = iter->base.path.len; level->dir = opendir(iter->base.path.buf); if (!level->dir && errno != ENOENT) { warning("error opening directory %s: %s", iter->base.path.buf, strerror(errno)); /* Popping the level is handled below */ } level->initialized = 1; } else if (S_ISDIR(iter->base.st.st_mode)) { if (level->dir_state == DIR_STATE_ITER) { /* * The directory was just iterated * over; now prepare to iterate into * it. */ level->dir_state = DIR_STATE_RECURSE; ALLOC_GROW(iter->levels, iter->levels_nr + 1, iter->levels_alloc); level = &iter->levels[iter->levels_nr++]; level->initialized = 0; continue; } else { /* * The directory has already been * iterated over and iterated into; * we're done with it. */ } } if (!level->dir) { /* * This level is exhausted (or wasn't opened * successfully); pop up a level. */ if (--iter->levels_nr == 0) return dir_iterator_abort(dir_iterator); continue; } /* * Loop until we find an entry that we can give back * to the caller: */ while (1) { strbuf_setlen(&iter->base.path, level->prefix_len); errno = 0; de = readdir(level->dir); if (!de) { /* This level is exhausted; pop up a level. */ if (errno) { warning("error reading directory %s: %s", iter->base.path.buf, strerror(errno)); } else if (closedir(level->dir)) warning("error closing directory %s: %s", iter->base.path.buf, strerror(errno)); level->dir = NULL; if (--iter->levels_nr == 0) return dir_iterator_abort(dir_iterator); break; } if (is_dot_or_dotdot(de->d_name)) continue; strbuf_addstr(&iter->base.path, de->d_name); if (lstat(iter->base.path.buf, &iter->base.st) < 0) { if (errno != ENOENT) warning("error reading path '%s': %s", iter->base.path.buf, strerror(errno)); continue; } /* * We have to set these each time because * the path strbuf might have been realloc()ed. */ iter->base.relative_path = iter->base.path.buf + iter->levels[0].prefix_len; iter->base.basename = iter->base.path.buf + level->prefix_len; level->dir_state = DIR_STATE_ITER; return ITER_OK; } } }
/* * Use this to get the real path, i.e. resolve links. If you want an * absolute path but don't mind links, use absolute_path. * * If path is our buffer, then return path, as it's already what the * user wants. */ const char *real_path(const char *path) { static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; char cwd[1024] = ""; int buf_index = 1; int depth = MAXDEPTH; char *last_elem = NULL; struct stat st; /* We've already done it */ if (path == buf || path == next_buf) return path; if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) die ("Too long path: %.*s", 60, path); while (depth--) { if (!is_directory(buf)) { char *last_slash = find_last_dir_sep(buf); if (last_slash) { *last_slash = '\0'; last_elem = xstrdup(last_slash + 1); } else { last_elem = xstrdup(buf); *buf = '\0'; } } if (*buf) { if (!*cwd && !getcwd(cwd, sizeof(cwd))) die_errno ("Could not get current working directory"); if (chdir(buf)) die_errno ("Could not switch to '%s'", buf); } if (!getcwd(buf, PATH_MAX)) die_errno ("Could not get current working directory"); if (last_elem) { size_t len = strlen(buf); if (len + strlen(last_elem) + 2 > PATH_MAX) die ("Too long path name: '%s/%s'", buf, last_elem); if (len && !is_dir_sep(buf[len-1])) buf[len++] = '/'; strcpy(buf + len, last_elem); free(last_elem); last_elem = NULL; } if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { ssize_t len = readlink(buf, next_buf, PATH_MAX); if (len < 0) die_errno ("Invalid symlink '%s'", buf); if (PATH_MAX <= len) die("symbolic link too long: %s", buf); next_buf[len] = '\0'; buf = next_buf; buf_index = 1 - buf_index; next_buf = bufs[buf_index]; } else break; } if (*cwd && chdir(cwd)) die_errno ("Could not change back to '%s'", cwd); return buf; }
static int starts_with_dot_dot_slash(const char *str) { return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]); }
/* * The `url` argument is the URL that navigates to the submodule origin * repo. When relative, this URL is relative to the superproject origin * URL repo. The `up_path` argument, if specified, is the relative * path that navigates from the submodule working tree to the superproject * working tree. Returns the origin URL of the submodule. * * Return either an absolute URL or filesystem path (if the superproject * origin URL is an absolute URL or filesystem path, respectively) or a * relative file system path (if the superproject origin URL is a relative * file system path). * * When the output is a relative file system path, the path is either * relative to the submodule working tree, if up_path is specified, or to * the superproject working tree otherwise. * * NEEDSWORK: This works incorrectly on the domain and protocol part. * remote_url url outcome expectation * http://a.com/b ../c http://a.com/c as is * http://a.com/b/ ../c http://a.com/c same as previous line, but * ignore trailing slash in url * http://a.com/b ../../c http://c error out * http://a.com/b ../../../c http:/c error out * http://a.com/b ../../../../c http:c error out * http://a.com/b ../../../../../c .:c error out * NEEDSWORK: Given how chop_last_dir() works, this function is broken * when a local part has a colon in its path component, too. */ static char *relative_url(const char *remote_url, const char *url, const char *up_path) { int is_relative = 0; int colonsep = 0; char *out; char *remoteurl = xstrdup(remote_url); struct strbuf sb = STRBUF_INIT; size_t len = strlen(remoteurl); if (is_dir_sep(remoteurl[len-1])) remoteurl[len-1] = '\0'; if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl)) is_relative = 0; else { is_relative = 1; /* * Prepend a './' to ensure all relative * remoteurls start with './' or '../' */ if (!starts_with_dot_slash(remoteurl) && !starts_with_dot_dot_slash(remoteurl)) { strbuf_reset(&sb); strbuf_addf(&sb, "./%s", remoteurl); free(remoteurl); remoteurl = strbuf_detach(&sb, NULL); } } /* * When the url starts with '../', remove that and the * last directory in remoteurl. */ while (url) { if (starts_with_dot_dot_slash(url)) { url += 3; colonsep |= chop_last_dir(&remoteurl, is_relative); } else if (starts_with_dot_slash(url)) url += 2; else break; } strbuf_reset(&sb); strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url); if (ends_with(url, "/")) strbuf_setlen(&sb, sb.len - 1); free(remoteurl); if (starts_with_dot_slash(sb.buf)) out = xstrdup(sb.buf + 2); else out = xstrdup(sb.buf); strbuf_reset(&sb); if (!up_path || !is_relative) return out; strbuf_addf(&sb, "%s%s", up_path, out); free(out); return strbuf_detach(&sb, NULL); }
/* * Return the real path (i.e., absolute path, with symlinks resolved * and extra slashes removed) equivalent to the specified path. (If * you want an absolute path but don't mind links, use * absolute_path().) The return value is a pointer to a static * buffer. * * The input and all intermediate paths must be shorter than MAX_PATH. * The directory part of path (i.e., everything up to the last * dir_sep) must denote a valid, existing directory, but the last * component need not exist. If die_on_error is set, then die with an * informative error message if there is a problem. Otherwise, return * NULL on errors (without generating any output). * * If path is our buffer, then return path, as it's already what the * user wants. */ static const char *real_path_internal(const char *path, int die_on_error) { static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; char *retval = NULL; /* * If we have to temporarily chdir(), store the original CWD * here so that we can chdir() back to it at the end of the * function: */ char cwd[1024] = ""; int buf_index = 1; int depth = MAXDEPTH; char *last_elem = NULL; struct stat st; /* We've already done it */ if (path == buf || path == next_buf) return path; if (!*path) { if (die_on_error) die("The empty string is not a valid path"); else goto error_out; } if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) { if (die_on_error) die("Too long path: %.*s", 60, path); else goto error_out; } while (depth--) { if (!is_directory(buf)) { char *last_slash = find_last_dir_sep(buf); if (last_slash) { last_elem = xstrdup(last_slash + 1); last_slash[1] = '\0'; } else { last_elem = xstrdup(buf); *buf = '\0'; } } if (*buf) { if (!*cwd && !getcwd(cwd, sizeof(cwd))) { if (die_on_error) die_errno("Could not get current working directory"); else goto error_out; } if (chdir(buf)) { if (die_on_error) die_errno("Could not switch to '%s'", buf); else goto error_out; } } if (!getcwd(buf, PATH_MAX)) { if (die_on_error) die_errno("Could not get current working directory"); else goto error_out; } if (last_elem) { size_t len = strlen(buf); if (len + strlen(last_elem) + 2 > PATH_MAX) { if (die_on_error) die("Too long path name: '%s/%s'", buf, last_elem); else goto error_out; } if (len && !is_dir_sep(buf[len - 1])) buf[len++] = '/'; strcpy(buf + len, last_elem); free(last_elem); last_elem = NULL; } if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { ssize_t len = readlink(buf, next_buf, PATH_MAX); if (len < 0) { if (die_on_error) die_errno("Invalid symlink '%s'", buf); else goto error_out; } if (PATH_MAX <= len) { if (die_on_error) die("symbolic link too long: %s", buf); else goto error_out; } next_buf[len] = '\0'; buf = next_buf; buf_index = 1 - buf_index; next_buf = bufs[buf_index]; } else break; } retval = buf; error_out: free(last_elem); if (*cwd && chdir(cwd)) die_errno ("Could not change back to '%s'", cwd); return retval; }
static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start, *ptr; size_t len; char *dir; /* * Skip scheme. */ start = strstr(repo, "://"); if (start == NULL) start = repo; else start += 3; /* * Skip authentication data. The stripping does happen * greedily, such that we strip up to the last '@' inside * the host part. */ for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) { if (*ptr == '@') start = ptr + 1; } /* * Strip trailing spaces, slashes and /.git */ while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) end--; if (end - start > 5 && is_dir_sep(end[-5]) && !strncmp(end - 4, ".git", 4)) { end -= 5; while (start < end && is_dir_sep(end[-1])) end--; } /* * Strip trailing port number if we've got only a * hostname (that is, there is no dir separator but a * colon). This check is required such that we do not * strip URI's like '/foo/bar:2222.git', which should * result in a dir '2222' being guessed due to backwards * compatibility. */ if (memchr(start, '/', end - start) == NULL && memchr(start, ':', end - start) != NULL) { ptr = end; while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':') ptr--; if (start < ptr && ptr[-1] == ':') end = ptr - 1; } /* * Find last component. To remain backwards compatible we * also regard colons as path separators, such that * cloning a repository 'foo:bar.git' would result in a * directory 'bar' being guessed. */ ptr = end; while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':') ptr--; start = ptr; /* * Strip .{bundle,git}. */ len = end - start; strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git"); if (!len || (len == 1 && *start == '/')) die(_("No directory name could be guessed.\n" "Please specify a directory on the command line")); if (is_bare) dir = xstrfmt("%.*s.git", (int)len, start); else dir = xstrndup(start, len); /* * Replace sequences of 'control' characters and whitespace * with one ascii space, remove leading and trailing spaces. */ if (*dir) { char *out = dir; int prev_space = 1 /* strip leading whitespace */; for (end = dir; *end; ++end) { char ch = *end; if ((unsigned char)ch < '\x20') ch = '\x20'; if (isspace(ch)) { if (prev_space) continue; prev_space = 1; } else prev_space = 0; *out++ = ch; } *out = '\0'; if (out > dir && prev_space) out[-1] = '\0'; } return dir; }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; const char **pathspec; char *seen; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); if (!index_only) setup_work_tree(); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); /* * Drop trailing directory separators from directories so we'll find * submodules in the index. */ for (i = 0; i < argc; i++) { size_t pathlen = strlen(argv[i]); if (pathlen && is_dir_sep(argv[i][pathlen - 1]) && is_directory(argv[i])) { do { pathlen--; } while (pathlen && is_dir_sep(argv[i][pathlen - 1])); argv[i] = xmemdupz(argv[i], pathlen); } } pathspec = get_pathspec(prefix, argv); refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; seen = xcalloc(i, 1); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); } if (pathspec) { const char *match; int seen_any = 0; for (i = 0; (match = pathspec[i]) != NULL ; i++) { if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), match); } } else { seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), *match ? match : "."); } if (! seen_any) exit(0); } /* * If not forced, the file, the index and the HEAD (if exists) * must match; but the file can already been removed, since * this sequence is a natural "novice" way: * * rm F; git rm F * * Further, if HEAD commit exists, "diff-index --cached" must * report no changes unless forced. */ if (!force) { unsigned char sha1[20]; if (get_sha1("HEAD", sha1)) hashclr(sha1); if (check_local_mod(sha1, index_only)) exit(1); } else if (!index_only) { if (check_submodules_use_gitfiles()) exit(1); } /* * First remove the names from the index: we won't commit * the index unless all of them succeed. */ for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (!quiet) printf("rm '%s'\n", path); if (remove_file_from_cache(path)) die(_("git rm: unable to remove %s"), path); } if (show_only) return 0; /* * Then, unless we used "--cached", remove the filenames from * the workspace. If we fail to remove the first one, we * abort the "git rm" (but once we've successfully removed * any file at all, we'll go ahead and commit to it all: * by then we've already committed ourselves and can't fail * in the middle) */ if (!index_only) { int removed = 0; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { if (is_empty_dir(path)) { if (!rmdir(path)) { removed = 1; continue; } } else { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; strbuf_release(&buf); continue; } strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } } if (!remove_path(path)) { removed = 1; continue; } if (!removed) die_errno("git rm: '%s'", path); } } 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 0; }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0, untracked = 0, opt_exclude = -1; int seen_dashdash = 0; int external_grep_allowed__ignored; const char *show_in_pager = NULL, *default_pager = "dummy"; struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; int dummy; int use_index = 1; int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; int allow_revs; struct option options[] = { OPT_BOOL(0, "cached", &cached, N_("search in index instead of in the work tree")), OPT_NEGBIT(0, "no-index", &use_index, N_("find in contents not managed by git"), 1), OPT_BOOL(0, "untracked", &untracked, N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, N_("ignore files specified via '.gitignore'"), 1), OPT_BOOL(0, "recurse-submodules", &recurse_submodules, N_("recursively search in each submodule")), OPT_GROUP(""), OPT_BOOL('v', "invert-match", &opt.invert, N_("show non-matching lines")), OPT_BOOL('i', "ignore-case", &opt.ignore_case, N_("case insensitive matching")), OPT_BOOL('w', "word-regexp", &opt.word_regexp, N_("match patterns only at word boundaries")), OPT_SET_INT('a', "text", &opt.binary, N_("process binary files as text"), GREP_BINARY_TEXT), OPT_SET_INT('I', NULL, &opt.binary, N_("don't match patterns in binary files"), GREP_BINARY_NOMATCH), OPT_BOOL(0, "textconv", &opt.allow_textconv, N_("process binary files with textconv filters")), { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), N_("descend at most <depth> levels"), PARSE_OPT_NONEG, NULL, 1 }, OPT_GROUP(""), OPT_SET_INT('E', "extended-regexp", &pattern_type_arg, N_("use extended POSIX regular expressions"), GREP_PATTERN_TYPE_ERE), OPT_SET_INT('G', "basic-regexp", &pattern_type_arg, N_("use basic POSIX regular expressions (default)"), GREP_PATTERN_TYPE_BRE), OPT_SET_INT('F', "fixed-strings", &pattern_type_arg, N_("interpret patterns as fixed strings"), GREP_PATTERN_TYPE_FIXED), OPT_SET_INT('P', "perl-regexp", &pattern_type_arg, N_("use Perl-compatible regular expressions"), GREP_PATTERN_TYPE_PCRE), OPT_GROUP(""), OPT_BOOL('n', "line-number", &opt.linenum, N_("show line numbers")), OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1), OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1), OPT_NEGBIT(0, "full-name", &opt.relative, N_("show filenames relative to top directory"), 1), OPT_BOOL('l', "files-with-matches", &opt.name_only, N_("show only filenames instead of matching lines")), OPT_BOOL(0, "name-only", &opt.name_only, N_("synonym for --files-with-matches")), OPT_BOOL('L', "files-without-match", &opt.unmatch_name_only, N_("show only the names of files without match")), OPT_BOOL('z', "null", &opt.null_following_name, N_("print NUL after filenames")), OPT_BOOL('c', "count", &opt.count, N_("show the number of matches instead of matching lines")), OPT__COLOR(&opt.color, N_("highlight matches")), OPT_BOOL(0, "break", &opt.file_break, N_("print empty line between matches from different files")), OPT_BOOL(0, "heading", &opt.heading, N_("show filename only once above matches from same file")), OPT_GROUP(""), OPT_CALLBACK('C', "context", &opt, N_("n"), N_("show <n> context lines before and after matches"), context_callback), OPT_INTEGER('B', "before-context", &opt.pre_context, N_("show <n> context lines before matches")), OPT_INTEGER('A', "after-context", &opt.post_context, N_("show <n> context lines after matches")), OPT_INTEGER(0, "threads", &num_threads, N_("use <n> worker threads")), OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), OPT_BOOL('p', "show-function", &opt.funcname, N_("show a line with the function name before matches")), OPT_BOOL('W', "function-context", &opt.funcbody, N_("show the surrounding function")), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, N_("file"), N_("read patterns from file"), file_callback), { OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"), N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOL(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, open_callback }, { OPTION_CALLBACK, ')', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, close_callback }, OPT__QUIET(&opt.status_only, N_("indicate hit with exit status without output")), OPT_BOOL(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), { OPTION_SET_INT, 0, "debug", &opt.debug, NULL, N_("show parse tree for grep expression"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 }, OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored, N_("allow calling of grep(1) (ignored by this build)")), OPT_END() }; init_grep_defaults(); git_config(grep_cmd_config, NULL); grep_init(&opt, prefix); /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or * -f, we take the first unrecognized non option to be the * pattern, but then what follows it must be zero or more * valid refs up to the -- (if exists), and then existing * paths. If there is an explicit pattern, then the first * unrecognized non option is the beginning of the refs list * that continues up to the -- (if exists), and then paths. */ argc = parse_options(argc, argv, prefix, options, grep_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION); grep_commit_pattern_type(pattern_type_arg, &opt); if (use_index && !startup_info->have_repository) { int fallback = 0; git_config_get_bool("grep.fallbacktonoindex", &fallback); if (fallback) use_index = 0; else /* die the same way as if we did it at the beginning */ setup_git_directory(); } /* * skip a -- separator; we know it cannot be * separating revisions from pathnames if * we haven't even had any patterns yet */ if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) { argv++; argc--; } /* First unrecognized non-option token */ if (argc > 0 && !opt.pattern_list) { append_grep_pattern(&opt, argv[0], "command line", 0, GREP_PATTERN); argv++; argc--; } if (show_in_pager == default_pager) show_in_pager = git_pager(1); if (show_in_pager) { opt.color = 0; opt.name_only = 1; opt.null_following_name = 1; opt.output_priv = &path_list; opt.output = append_path; string_list_append(&path_list, show_in_pager); } if (!opt.pattern_list) die(_("no pattern given.")); /* * We have to find "--" in a separate pass, because its presence * influences how we will parse arguments that come before it. */ for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "--")) { seen_dashdash = 1; break; } } /* * Resolve any rev arguments. If we have a dashdash, then everything up * to it must resolve as a rev. If not, then we stop at the first * non-rev and assume everything else is a path. */ allow_revs = use_index && !untracked; for (i = 0; i < argc; i++) { const char *arg = argv[i]; struct object_id oid; struct object_context oc; struct object *object; if (!strcmp(arg, "--")) { i++; break; } if (!allow_revs) { if (seen_dashdash) die(_("--no-index or --untracked cannot be used with revs")); break; } if (get_oid_with_context(arg, GET_OID_RECORD_PATH, &oid, &oc)) { if (seen_dashdash) die(_("unable to resolve revision: %s"), arg); break; } object = parse_object_or_die(&oid, arg); if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); free(oc.path); } /* * Anything left over is presumed to be a path. But in the non-dashdash * "do what I mean" case, we verify and complain when that isn't true. */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j], j == i && allow_revs); } parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD | (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), prefix, argv + i); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; pathspec.recurse_submodules = !!recurse_submodules; #ifndef NO_PTHREADS if (list.nr || cached || show_in_pager) num_threads = 0; else if (num_threads == 0) num_threads = GREP_NUM_THREADS_DEFAULT; else if (num_threads < 0) die(_("invalid number of threads specified (%d)"), num_threads); if (num_threads == 1) num_threads = 0; #else if (num_threads) warning(_("no threads support, ignoring --threads")); num_threads = 0; #endif if (!num_threads) /* * The compiled patterns on the main path are only * used when not using threading. Otherwise * start_threads() below calls compile_grep_patterns() * for each thread. */ compile_grep_patterns(&opt); #ifndef NO_PTHREADS if (num_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count) && (opt.pre_context || opt.post_context || opt.file_break || opt.funcbody)) skip_first_line = 1; start_threads(&opt); } #endif if (show_in_pager && (cached || list.nr)) die(_("--open-files-in-pager only works on the worktree")); if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) { const char *pager = path_list.items[0].string; int len = strlen(pager); if (len > 4 && is_dir_sep(pager[len - 5])) pager += len - 4; if (opt.ignore_case && !strcmp("less", pager)) string_list_append(&path_list, "-I"); if (!strcmp("less", pager) || !strcmp("vi", pager)) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "+/%s%s", strcmp("less", pager) ? "" : "*", opt.pattern_list->pattern); string_list_append(&path_list, buf.buf); strbuf_detach(&buf, NULL); } } if (recurse_submodules && (!use_index || untracked)) die(_("option not supported with --recurse-submodules.")); if (!show_in_pager && !opt.status_only) setup_pager(); if (!use_index && (untracked || cached)) die(_("--cached or --untracked cannot be used with --no-index.")); if (!use_index || untracked) { int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; hit = grep_directory(&opt, &pathspec, use_exclude, use_index); } else if (0 <= opt_exclude) { die(_("--[no-]exclude-standard cannot be used for tracked contents.")); } else if (!list.nr) { if (!cached) setup_work_tree(); hit = grep_cache(&opt, the_repository, &pathspec, cached); } else { if (cached) die(_("both --cached and trees are given.")); hit = grep_objects(&opt, &pathspec, the_repository, &list); } if (num_threads) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); clear_pathspec(&pathspec); free_grep_patterns(&opt); return !hit; }
/* * It is okay if dst == src, but they should not overlap otherwise. * * Performs the following normalizations on src, storing the result in dst: * - Ensures that components are separated by '/' (Windows only) * - Squashes sequences of '/' except "//server/share" on Windows * - Removes "." components. * - Removes ".." components, and the components the precede them. * Returns failure (non-zero) if a ".." component appears as first path * component anytime during the normalization. Otherwise, returns success (0). * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. * * prefix_len != NULL is for a specific case of prefix_pathspec(): * assume that src == dst and src[0..prefix_len-1] is already * normalized, any time "../" eats up to the prefix_len part, * prefix_len is reduced. In the end prefix_len is the remaining * prefix that has not been overridden by user pathspec. * * NEEDSWORK: This function doesn't perform normalization w.r.t. trailing '/'. * For everything but the root folder itself, the normalized path should not * end with a '/', then the callers need to be fixed up accordingly. * */ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; const char *end; /* * Copy initial part of absolute path: "/", "C:/", "//server/share/". */ end = src + offset_1st_component(src); while (src < end) { char c = *src++; if (is_dir_sep(c)) c = '/'; *dst++ = c; } dst0 = dst; while (is_dir_sep(*src)) src++; for (;;) { char c = *src; /* * A path component that begins with . could be * special: * (1) "." and ends -- ignore and terminate. * (2) "./" -- ignore them, eat slash and continue. * (3) ".." and ends -- strip one and terminate. * (4) "../" -- strip one, eat slash and continue. */ if (c == '.') { if (!src[1]) { /* (1) */ src++; } else if (is_dir_sep(src[1])) { /* (2) */ src += 2; while (is_dir_sep(*src)) src++; continue; } else if (src[1] == '.') { if (!src[2]) { /* (3) */ src += 2; goto up_one; } else if (is_dir_sep(src[2])) { /* (4) */ src += 3; while (is_dir_sep(*src)) src++; goto up_one; } } } /* copy up to the next '/', and eat all '/' */ while ((c = *src++) != '\0' && !is_dir_sep(c)) *dst++ = c; if (is_dir_sep(c)) { *dst++ = '/'; while (is_dir_sep(c)) c = *src++; src--; } else if (!c) break; continue; up_one: /* * dst0..dst is prefix portion, and dst[-1] is '/'; * go up one level. */ dst--; /* go to trailing '/' */ if (dst <= dst0) return -1; /* Windows: dst[-1] cannot be backslash anymore */ while (dst0 < dst && dst[-1] != '/') dst--; if (prefix_len && *prefix_len > dst - dst0) *prefix_len = dst - dst0; } *dst = '\0'; return 0; }
void strbuf_trim_trailing_dir_sep(struct strbuf *sb) { while (sb->len > 0 && is_dir_sep((unsigned char)sb->buf[sb->len - 1])) sb->len--; sb->buf[sb->len] = '\0'; }
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, int argc, const char **argv) { char tmpdir[PATH_MAX]; struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT; struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT; struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT; struct strbuf wtdir = STRBUF_INIT; char *lbase_dir, *rbase_dir; size_t ldir_len, rdir_len, wtdir_len; const char *workdir, *tmp; int ret = 0, i; FILE *fp; struct hashmap working_tree_dups, submodules, symlinks2; struct hashmap_iter iter; struct pair_entry *entry; struct index_state wtindex; struct checkout lstate, rstate; int rc, flags = RUN_GIT_CMD, err = 0; struct child_process child = CHILD_PROCESS_INIT; const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; workdir = get_git_work_tree(); /* Setup temp directories */ tmp = getenv("TMPDIR"); xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp"); if (!mkdtemp(tmpdir)) return error("could not create '%s'", tmpdir); strbuf_addf(&ldir, "%s/left/", tmpdir); strbuf_addf(&rdir, "%s/right/", tmpdir); strbuf_addstr(&wtdir, workdir); if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1])) strbuf_addch(&wtdir, '/'); mkdir(ldir.buf, 0700); mkdir(rdir.buf, 0700); memset(&wtindex, 0, sizeof(wtindex)); memset(&lstate, 0, sizeof(lstate)); lstate.base_dir = lbase_dir = xstrdup(ldir.buf); lstate.base_dir_len = ldir.len; lstate.force = 1; memset(&rstate, 0, sizeof(rstate)); rstate.base_dir = rbase_dir = xstrdup(rdir.buf); rstate.base_dir_len = rdir.len; rstate.force = 1; ldir_len = ldir.len; rdir_len = rdir.len; wtdir_len = wtdir.len; hashmap_init(&working_tree_dups, (hashmap_cmp_fn)working_tree_entry_cmp, NULL, 0); hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, NULL, 0); hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, NULL, 0); child.no_stdin = 1; child.git_cmd = 1; child.use_shell = 0; child.clean_on_exit = 1; child.dir = prefix; child.out = -1; argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z", NULL); for (i = 0; i < argc; i++) argv_array_push(&child.args, argv[i]); if (start_command(&child)) die("could not obtain raw diff"); fp = xfdopen(child.out, "r"); /* Build index info for left and right sides of the diff */ i = 0; while (!strbuf_getline_nul(&info, fp)) { int lmode, rmode; struct object_id loid, roid; char status; const char *src_path, *dst_path; if (starts_with(info.buf, "::")) die(N_("combined diff formats('-c' and '--cc') are " "not supported in\n" "directory diff mode('-d' and '--dir-diff').")); if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid, &status)) break; if (strbuf_getline_nul(&lpath, fp)) break; src_path = lpath.buf; i++; if (status != 'C' && status != 'R') { dst_path = src_path; } else { if (strbuf_getline_nul(&rpath, fp)) break; dst_path = rpath.buf; } if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) { strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&loid)); add_left_or_right(&submodules, src_path, buf.buf, 0); strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&roid)); if (!oidcmp(&loid, &roid)) strbuf_addstr(&buf, "-dirty"); add_left_or_right(&submodules, dst_path, buf.buf, 1); continue; } if (S_ISLNK(lmode)) { char *content = get_symlink(&loid, src_path); add_left_or_right(&symlinks2, src_path, content, 0); free(content); } if (S_ISLNK(rmode)) { char *content = get_symlink(&roid, dst_path); add_left_or_right(&symlinks2, dst_path, content, 1); free(content); } if (lmode && status != 'C') { if (checkout_path(lmode, &loid, src_path, &lstate)) { ret = error("could not write '%s'", src_path); goto finish; } } if (rmode && !S_ISLNK(rmode)) { struct working_tree_entry *entry; /* Avoid duplicate working_tree entries */ FLEX_ALLOC_STR(entry, path, dst_path); hashmap_entry_init(entry, strhash(dst_path)); if (hashmap_get(&working_tree_dups, entry, NULL)) { free(entry); continue; } hashmap_add(&working_tree_dups, entry); if (!use_wt_file(workdir, dst_path, &roid)) { if (checkout_path(rmode, &roid, dst_path, &rstate)) { ret = error("could not write '%s'", dst_path); goto finish; } } else if (!is_null_oid(&roid)) { /* * Changes in the working tree need special * treatment since they are not part of the * index. */ struct cache_entry *ce2 = make_cache_entry(rmode, roid.hash, dst_path, 0, 0); add_index_entry(&wtindex, ce2, ADD_CACHE_JUST_APPEND); add_path(&rdir, rdir_len, dst_path); if (ensure_leading_directories(rdir.buf)) { ret = error("could not create " "directory for '%s'", dst_path); goto finish; } add_path(&wtdir, wtdir_len, dst_path); if (symlinks) { if (symlink(wtdir.buf, rdir.buf)) { ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } else { struct stat st; if (stat(wtdir.buf, &st)) st.st_mode = 0644; if (copy_file(rdir.buf, wtdir.buf, st.st_mode)) { ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } } } } fclose(fp); fp = NULL; if (finish_command(&child)) { ret = error("error occurred running diff --raw"); goto finish; } if (!i) goto finish; /* * Changes to submodules require special treatment.This loop writes a * temporary file to both the left and right directories to show the * change in the recorded SHA1 for the submodule. */ hashmap_iter_init(&submodules, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } /* * Symbolic links require special treatment.The standard "git diff" * shows only the link itself, not the contents of the link target. * This loop replicates that behavior. */ hashmap_iter_init(&symlinks2, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } strbuf_release(&buf); strbuf_setlen(&ldir, ldir_len); helper_argv[1] = ldir.buf; strbuf_setlen(&rdir, rdir_len); helper_argv[2] = rdir.buf; if (extcmd) { helper_argv[0] = extcmd; flags = 0; } else setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1); rc = run_command_v_opt(helper_argv, flags); /* * If the diff includes working copy files and those * files were modified during the diff, then the changes * should be copied back to the working tree. * Do not copy back files when symlinks are used and the * external tool did not replace the original link with a file. * * These hashes are loaded lazily since they aren't needed * in the common case of --symlinks and the difftool updating * files through the symlink. */ hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; const char *name = wtindex.cache[i]->name; struct stat st; add_path(&rdir, rdir_len, name); if (lstat(rdir.buf, &st)) continue; if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode)) continue; if (!indices_loaded) { static struct lock_file lock; strbuf_reset(&buf); strbuf_addf(&buf, "%s/wtindex", tmpdir); if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || write_locked_index(&wtindex, &lock, COMMIT_LOCK)) { ret = error("could not write %s", buf.buf); rollback_lock_file(&lock); goto finish; } changed_files(&wt_modified, buf.buf, workdir); strbuf_setlen(&rdir, rdir_len); changed_files(&tmp_modified, buf.buf, rdir.buf); add_path(&rdir, rdir_len, name); indices_loaded = 1; } hashmap_entry_init(&dummy, strhash(name)); if (hashmap_get(&tmp_modified, &dummy, name)) { add_path(&wtdir, wtdir_len, name); if (hashmap_get(&wt_modified, &dummy, name)) { warning(_("both files modified: '%s' and '%s'."), wtdir.buf, rdir.buf); warning(_("working tree file has been left.")); warning("%s", ""); err = 1; } else if (unlink(wtdir.buf) || copy_file(wtdir.buf, rdir.buf, st.st_mode)) warning_errno(_("could not copy '%s' to '%s'"), rdir.buf, wtdir.buf); } } if (err) { warning(_("temporary files exist in '%s'."), tmpdir); warning(_("you may want to cleanup or recover these.")); exit(1); } else exit_cleanup(tmpdir, rc); finish: if (fp) fclose(fp); free(lbase_dir); free(rbase_dir); strbuf_release(&ldir); strbuf_release(&rdir); strbuf_release(&wtdir); strbuf_release(&buf); return ret; }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, flags, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache() < 0) die(_("index file corrupt")); source = internal_prefix_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out, except in the case * "git mv directory no-such-dir/". */ flags = KEEP_TRAILING_SLASH; if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die(_("destination '%s' is not a directory"), dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length), last; if (first >= 0) prepare_move_submodule(src, first, submodule_gitfile + i); else if (index_range_of_same_dir(src, length, &first, &last) < 1) bad = _("source directory is empty"); else { /* last - first >= 1 */ int j, dst_len, n; modes[i] = WORKING_DIRECTORY; n = argc + last - first; REALLOC_ARRAY(source, n); REALLOC_ARRAY(destination, n); REALLOC_ARRAY(modes, n); REALLOC_ARRAY(submodule_gitfile, n); dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0 && (!ignore_case || strcasecmp(src, dst))) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (!bad) continue; if (!ignore_errors) die(_("%s, source=%s, destination=%s"), bad, src, dst); if (--argc > 0) { int n = argc - i; memmove(source + i, source + i + 1, n * sizeof(char *)); memmove(destination + i, destination + i + 1, n * sizeof(char *)); memmove(modes + i, modes + i + 1, n * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, n * sizeof(char *)); i--; } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (show_only) continue; if (mode != INDEX && rename(src, dst) < 0) { if (ignore_errors) continue; die_errno(_("renaming '%s' failed"), src); } if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); if (active_cache_changed && write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); return 0; }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0, untracked = 0, opt_exclude = -1; int seen_dashdash = 0; int external_grep_allowed__ignored; const char *show_in_pager = NULL, *default_pager = "dummy"; struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; const char **paths = NULL; struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; int dummy; int use_index = 1; int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; struct option options[] = { OPT_BOOLEAN(0, "cached", &cached, N_("search in index instead of in the work tree")), OPT_NEGBIT(0, "no-index", &use_index, N_("find in contents not managed by git"), 1), OPT_BOOLEAN(0, "untracked", &untracked, N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, N_("search also in ignored files"), 1), OPT_GROUP(""), OPT_BOOLEAN('v', "invert-match", &opt.invert, N_("show non-matching lines")), OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, N_("case insensitive matching")), OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, N_("match patterns only at word boundaries")), OPT_SET_INT('a', "text", &opt.binary, N_("process binary files as text"), GREP_BINARY_TEXT), OPT_SET_INT('I', NULL, &opt.binary, N_("don't match patterns in binary files"), GREP_BINARY_NOMATCH), { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), N_("descend at most <depth> levels"), PARSE_OPT_NONEG, NULL, 1 }, OPT_GROUP(""), OPT_SET_INT('E', "extended-regexp", &pattern_type_arg, N_("use extended POSIX regular expressions"), GREP_PATTERN_TYPE_ERE), OPT_SET_INT('G', "basic-regexp", &pattern_type_arg, N_("use basic POSIX regular expressions (default)"), GREP_PATTERN_TYPE_BRE), OPT_SET_INT('F', "fixed-strings", &pattern_type_arg, N_("interpret patterns as fixed strings"), GREP_PATTERN_TYPE_FIXED), OPT_SET_INT('P', "perl-regexp", &pattern_type_arg, N_("use Perl-compatible regular expressions"), GREP_PATTERN_TYPE_PCRE), OPT_GROUP(""), OPT_BOOLEAN('n', "line-number", &opt.linenum, N_("show line numbers")), OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1), OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1), OPT_NEGBIT(0, "full-name", &opt.relative, N_("show filenames relative to top directory"), 1), OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, N_("show only filenames instead of matching lines")), OPT_BOOLEAN(0, "name-only", &opt.name_only, N_("synonym for --files-with-matches")), OPT_BOOLEAN('L', "files-without-match", &opt.unmatch_name_only, N_("show only the names of files without match")), OPT_BOOLEAN('z', "null", &opt.null_following_name, N_("print NUL after filenames")), OPT_BOOLEAN('c', "count", &opt.count, N_("show the number of matches instead of matching lines")), OPT__COLOR(&opt.color, N_("highlight matches")), OPT_BOOLEAN(0, "break", &opt.file_break, N_("print empty line between matches from different files")), OPT_BOOLEAN(0, "heading", &opt.heading, N_("show filename only once above matches from same file")), OPT_GROUP(""), OPT_CALLBACK('C', "context", &opt, N_("n"), N_("show <n> context lines before and after matches"), context_callback), OPT_INTEGER('B', "before-context", &opt.pre_context, N_("show <n> context lines before matches")), OPT_INTEGER('A', "after-context", &opt.post_context, N_("show <n> context lines after matches")), OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), OPT_BOOLEAN('p', "show-function", &opt.funcname, N_("show a line with the function name before matches")), OPT_BOOLEAN('W', "function-context", &opt.funcbody, N_("show the surrounding function")), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, N_("file"), N_("read patterns from file"), file_callback), { OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"), N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOLEAN(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, open_callback }, { OPTION_CALLBACK, ')', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, close_callback }, OPT__QUIET(&opt.status_only, N_("indicate hit with exit status without output")), OPT_BOOLEAN(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), { OPTION_SET_INT, 0, "debug", &opt.debug, NULL, N_("show parse tree for grep expression"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 }, OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored, N_("allow calling of grep(1) (ignored by this build)")), { OPTION_CALLBACK, 0, "help-all", &options, NULL, N_("show usage"), PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() }; /* * 'git grep -h', unlike 'git grep -h <pattern>', is a request * to show usage information and exit. */ if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(grep_usage, options); init_grep_defaults(); git_config(grep_cmd_config, NULL); grep_init(&opt, prefix); /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or * -f, we take the first unrecognized non option to be the * pattern, but then what follows it must be zero or more * valid refs up to the -- (if exists), and then existing * paths. If there is an explicit pattern, then the first * unrecognized non option is the beginning of the refs list * that continues up to the -- (if exists), and then paths. */ argc = parse_options(argc, argv, prefix, options, grep_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION | PARSE_OPT_NO_INTERNAL_HELP); grep_commit_pattern_type(pattern_type_arg, &opt); if (use_index && !startup_info->have_repository) /* die the same way as if we did it at the beginning */ setup_git_directory(); /* * skip a -- separator; we know it cannot be * separating revisions from pathnames if * we haven't even had any patterns yet */ if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) { argv++; argc--; } /* First unrecognized non-option token */ if (argc > 0 && !opt.pattern_list) { append_grep_pattern(&opt, argv[0], "command line", 0, GREP_PATTERN); argv++; argc--; } if (show_in_pager == default_pager) show_in_pager = git_pager(1); if (show_in_pager) { opt.color = 0; opt.name_only = 1; opt.null_following_name = 1; opt.output_priv = &path_list; opt.output = append_path; string_list_append(&path_list, show_in_pager); use_threads = 0; } if ((opt.binary & GREP_BINARY_NOMATCH)) use_threads = 0; if (!opt.pattern_list) die(_("no pattern given.")); if (!opt.fixed && opt.ignore_case) opt.regflags |= REG_ICASE; compile_grep_patterns(&opt); /* Check revs and then paths */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) die(_("bad object %s"), arg); add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } #ifndef NO_PTHREADS if (list.nr || cached || online_cpus() == 1) use_threads = 0; #else use_threads = 0; #endif #ifndef NO_PTHREADS if (use_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count) && (opt.pre_context || opt.post_context || opt.file_break || opt.funcbody)) skip_first_line = 1; start_threads(&opt); } #endif /* The rest are paths */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j], j == i); } paths = get_pathspec(prefix, argv + i); init_pathspec(&pathspec, paths); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; if (show_in_pager && (cached || list.nr)) die(_("--open-files-in-pager only works on the worktree")); if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) { const char *pager = path_list.items[0].string; int len = strlen(pager); if (len > 4 && is_dir_sep(pager[len - 5])) pager += len - 4; if (opt.ignore_case && !strcmp("less", pager)) string_list_append(&path_list, "-i"); if (!strcmp("less", pager) || !strcmp("vi", pager)) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "+/%s%s", strcmp("less", pager) ? "" : "*", opt.pattern_list->pattern); string_list_append(&path_list, buf.buf); strbuf_detach(&buf, NULL); } } if (!show_in_pager) setup_pager(); if (!use_index && (untracked || cached)) die(_("--cached or --untracked cannot be used with --no-index.")); if (!use_index || untracked) { int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; if (list.nr) die(_("--no-index or --untracked cannot be used with revs.")); hit = grep_directory(&opt, &pathspec, use_exclude); } else if (0 <= opt_exclude) { die(_("--[no-]exclude-standard cannot be used for tracked contents.")); } else if (!list.nr) { if (!cached) setup_work_tree(); hit = grep_cache(&opt, &pathspec, cached); } else { if (cached) die(_("both --cached and trees are given.")); hit = grep_objects(&opt, &pathspec, &list); } if (use_threads) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); free_grep_patterns(&opt); return !hit; }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, newfd, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out. */ dest_path = internal_copy_pathspec(prefix, argv + argc, 1, KEEP_TRAILING_SLASH); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length); if (first >= 0) { struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die (_("Huh? Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok()) die (_("Please, stage your changes to .gitmodules or stash them to proceed")); strbuf_addf(&submodule_dotgit, "%s/.git", src); submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); if (submodule_gitfile[i]) submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); else submodule_gitfile[i] = SUBMODULE_WITH_GITDIR; strbuf_release(&submodule_dotgit); } else { const char *src_w_slash = add_slash(src); int last, len_w_slash = length + 1; modes[i] = WORKING_DIRECTORY; first = cache_name_pos(src_w_slash, len_w_slash); if (first >= 0) die (_("Huh? %.*s is in index?"), len_w_slash, src_w_slash); first = -1 - first; for (last = first; last < active_nr; last++) { const char *path = active_cache[last]->name; if (strncmp(path, src_w_slash, len_w_slash)) break; } free((char *)src_w_slash); if (last - first < 1) bad = _("source directory is empty"); else { int j, dst_len; if (last - first > 0) { source = xrealloc(source, (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); submodule_gitfile = xrealloc(submodule_gitfile, (argc + last - first) * sizeof(char *)); } dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (bad) { if (ignore_errors) { if (--argc > 0) { memmove(source + i, source + i + 1, (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); memmove(modes + i, modes + i + 1, (argc - i) * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, (argc - i) * sizeof(char *)); i--; } } else die (_("%s, source=%s, destination=%s"), bad, src, dst); } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (!show_only && mode != INDEX) { if (rename(src, dst) < 0 && !ignore_errors) die_errno (_("renaming '%s' failed"), src); if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); 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 0; }
/* strip arbitrary amount of directory separators at end of path */ static inline int chomp_trailing_dir_sep(const char *path, int len) { while (len && is_dir_sep(path[len - 1])) len--; return len; }
int offset_1st_component(const char *path) { if (has_dos_drive_prefix(path)) return 2 + is_dir_sep(path[2]); return is_dir_sep(path[0]); }
/* * Give path as relative to prefix. * * The strbuf may or may not be used, so do not assume it contains the * returned path. */ const char *relative_path(const char *in, const char *prefix, struct strbuf *sb) { int in_len = in ? strlen(in) : 0; int prefix_len = prefix ? strlen(prefix) : 0; int in_off = 0; int prefix_off = 0; int i = 0, j = 0; if (!in_len) return "./"; else if (!prefix_len) return in; if (have_same_root(in, prefix)) /* bypass dos_drive, for "c:" is identical to "C:" */ i = j = has_dos_drive_prefix(in); else { return in; } while (i < prefix_len && j < in_len && prefix[i] == in[j]) { if (is_dir_sep(prefix[i])) { while (is_dir_sep(prefix[i])) i++; while (is_dir_sep(in[j])) j++; prefix_off = i; in_off = j; } else { i++; j++; } } if ( /* "prefix" seems like prefix of "in" */ i >= prefix_len && /* * but "/foo" is not a prefix of "/foobar" * (i.e. prefix not end with '/') */ prefix_off < prefix_len) { if (j >= in_len) { /* in="/a/b", prefix="/a/b" */ in_off = in_len; } else if (is_dir_sep(in[j])) { /* in="/a/b/c", prefix="/a/b" */ while (is_dir_sep(in[j])) j++; in_off = j; } else { /* in="/a/bbb/c", prefix="/a/b" */ i = prefix_off; } } else if ( /* "in" is short than "prefix" */ j >= in_len && /* "in" not end with '/' */ in_off < in_len) { if (is_dir_sep(prefix[i])) { /* in="/a/b", prefix="/a/b/c/" */ while (is_dir_sep(prefix[i])) i++; in_off = in_len; } } in += in_off; in_len -= in_off; if (i >= prefix_len) { if (!in_len) return "./"; else return in; } strbuf_reset(sb); strbuf_grow(sb, in_len); while (i < prefix_len) { if (is_dir_sep(prefix[i])) { strbuf_addstr(sb, "../"); while (is_dir_sep(prefix[i])) i++; continue; } i++; } if (!is_dir_sep(prefix[prefix_len - 1])) strbuf_addstr(sb, "../"); strbuf_addstr(sb, in); return sb->buf; }
static int sanitary_path_copy(char *dst, const char *src) { char *dst0; if (has_dos_drive_prefix(src)) { *dst++ = *src++; *dst++ = *src++; } dst0 = dst; if (is_dir_sep(*src)) { *dst++ = '/'; while (is_dir_sep(*src)) src++; } for (;;) { char c = *src; /* * A path component that begins with . could be * special: * (1) "." and ends -- ignore and terminate. * (2) "./" -- ignore them, eat slash and continue. * (3) ".." and ends -- strip one and terminate. * (4) "../" -- strip one, eat slash and continue. */ if (c == '.') { if (!src[1]) { /* (1) */ src++; } else if (is_dir_sep(src[1])) { /* (2) */ src += 2; while (is_dir_sep(*src)) src++; continue; } else if (src[1] == '.') { if (!src[2]) { /* (3) */ src += 2; goto up_one; } else if (is_dir_sep(src[2])) { /* (4) */ src += 3; while (is_dir_sep(*src)) src++; goto up_one; } } } /* copy up to the next '/', and eat all '/' */ while ((c = *src++) != '\0' && !is_dir_sep(c)) *dst++ = c; if (is_dir_sep(c)) { *dst++ = '/'; while (is_dir_sep(c)) c = *src++; src--; } else if (!c) break; continue; up_one: /* * dst0..dst is prefix portion, and dst[-1] is '/'; * go up one level. */ dst -= 2; /* go past trailing '/' if any */ if (dst < dst0) return -1; while (1) { if (dst <= dst0) break; c = *dst--; if (c == '/') { /* MinGW: cannot be '\\' anymore */ dst += 2; break; } } } *dst = '\0'; return 0; }