}
free(another);
} else {
- if (!starts_with(line, "/dev/null\n"))
+ if (!is_dev_null(line))
return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
}
static int read_old_data(struct stat *st, struct patch *patch,
const char *path, struct strbuf *buf)
{
- enum safe_crlf safe_crlf = patch->crlf_in_old ?
- SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
+ int conv_flags = patch->crlf_in_old ?
+ CONV_EOL_KEEP_CRLF : CONV_EOL_RENORMALIZE;
switch (st->st_mode & S_IFMT) {
case S_IFLNK:
if (strbuf_readlink(buf, path, st->st_size) < 0)
* should never look at the index when explicit crlf option
* is given.
*/
- convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
+ convert_to_git(NULL, path, buf->buf, buf->len, buf, conv_flags);
return 0;
default:
return -1;
size_t len, size_t postlen)
{
int i, ctx, reduced;
- char *new, *old, *fixed;
+ char *new_buf, *old_buf, *fixed;
struct image fixed_preimage;
/*
* We trust the caller to tell us if the update can be done
* in place (postlen==0) or not.
*/
- old = postimage->buf;
+ old_buf = postimage->buf;
if (postlen)
- new = postimage->buf = xmalloc(postlen);
+ new_buf = postimage->buf = xmalloc(postlen);
else
- new = old;
+ new_buf = old_buf;
fixed = preimage->buf;
for (i = reduced = ctx = 0; i < postimage->nr; i++) {
size_t l_len = postimage->line[i].len;
if (!(postimage->line[i].flag & LINE_COMMON)) {
/* an added line -- no counterparts in preimage */
- memmove(new, old, l_len);
- old += l_len;
- new += l_len;
+ memmove(new_buf, old_buf, l_len);
+ old_buf += l_len;
+ new_buf += l_len;
continue;
}
/* a common context -- skip it in the original postimage */
- old += l_len;
+ old_buf += l_len;
/* and find the corresponding one in the fixed preimage */
while (ctx < preimage->nr &&
/* and copy it in, while fixing the line length */
l_len = preimage->line[ctx].len;
- memcpy(new, fixed, l_len);
- new += l_len;
+ memcpy(new_buf, fixed, l_len);
+ new_buf += l_len;
fixed += l_len;
postimage->line[i].len = l_len;
ctx++;
}
if (postlen
- ? postlen < new - postimage->buf
- : postimage->len < new - postimage->buf)
+ ? postlen < new_buf - postimage->buf
+ : postimage->len < new_buf - postimage->buf)
die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d",
- (int)postlen, (int) postimage->len, (int)(new - postimage->buf));
+ (int)postlen, (int) postimage->len, (int)(new_buf - postimage->buf));
/* Fix the length of the whole thing */
- postimage->len = new - postimage->buf;
+ postimage->len = new_buf - postimage->buf;
postimage->nr -= reduced;
}
static int line_by_line_fuzzy_match(struct image *img,
struct image *preimage,
struct image *postimage,
- unsigned long try,
- int try_lno,
+ unsigned long current,
+ int current_lno,
int preimage_limit)
{
int i;
for (i = 0; i < preimage_limit; i++) {
size_t prelen = preimage->line[i].len;
- size_t imglen = img->line[try_lno+i].len;
+ size_t imglen = img->line[current_lno+i].len;
- if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+ if (!fuzzy_matchlines(img->buf + current + imgoff, imglen,
preimage->buf + preoff, prelen))
return 0;
if (preimage->line[i].flag & LINE_COMMON)
*/
extra_chars = preimage_end - preimage_eof;
strbuf_init(&fixed, imgoff + extra_chars);
- strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, img->buf + current, imgoff);
strbuf_add(&fixed, preimage_eof, extra_chars);
fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
struct image *img,
struct image *preimage,
struct image *postimage,
- unsigned long try,
- int try_lno,
+ unsigned long current,
+ int current_lno,
unsigned ws_rule,
int match_beginning, int match_end)
{
size_t fixed_len, postlen;
int preimage_limit;
- if (preimage->nr + try_lno <= img->nr) {
+ if (preimage->nr + current_lno <= img->nr) {
/*
* The hunk falls within the boundaries of img.
*/
preimage_limit = preimage->nr;
- if (match_end && (preimage->nr + try_lno != img->nr))
+ if (match_end && (preimage->nr + current_lno != img->nr))
return 0;
} else if (state->ws_error_action == correct_ws_error &&
(ws_rule & WS_BLANK_AT_EOF)) {
* match with img, and the remainder of the preimage
* must be blank.
*/
- preimage_limit = img->nr - try_lno;
+ preimage_limit = img->nr - current_lno;
} else {
/*
* The hunk extends beyond the end of the img and
return 0;
}
- if (match_beginning && try_lno)
+ if (match_beginning && current_lno)
return 0;
/* Quick hash check */
for (i = 0; i < preimage_limit; i++)
- if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
- (preimage->line[i].hash != img->line[try_lno + i].hash))
+ if ((img->line[current_lno + i].flag & LINE_PATCHED) ||
+ (preimage->line[i].hash != img->line[current_lno + i].hash))
return 0;
if (preimage_limit == preimage->nr) {
/*
* Do we have an exact match? If we were told to match
- * at the end, size must be exactly at try+fragsize,
- * otherwise try+fragsize must be still within the preimage,
+ * at the end, size must be exactly at current+fragsize,
+ * otherwise current+fragsize must be still within the preimage,
* and either case, the old piece should match the preimage
* exactly.
*/
if ((match_end
- ? (try + preimage->len == img->len)
- : (try + preimage->len <= img->len)) &&
- !memcmp(img->buf + try, preimage->buf, preimage->len))
+ ? (current + preimage->len == img->len)
+ : (current + preimage->len <= img->len)) &&
+ !memcmp(img->buf + current, preimage->buf, preimage->len))
return 1;
} else {
/*
*/
if (state->ws_ignore_action == ignore_ws_change)
return line_by_line_fuzzy_match(img, preimage, postimage,
- try, try_lno, preimage_limit);
+ current, current_lno, preimage_limit);
if (state->ws_error_action != correct_ws_error)
return 0;
*/
strbuf_init(&fixed, preimage->len + 1);
orig = preimage->buf;
- target = img->buf + try;
+ target = img->buf + current;
for (i = 0; i < preimage_limit; i++) {
size_t oldlen = preimage->line[i].len;
- size_t tgtlen = img->line[try_lno + i].len;
+ size_t tgtlen = img->line[current_lno + i].len;
size_t fixstart = fixed.len;
struct strbuf tgtfix;
int match;
int match_beginning, int match_end)
{
int i;
- unsigned long backwards, forwards, try;
- int backwards_lno, forwards_lno, try_lno;
+ unsigned long backwards, forwards, current;
+ int backwards_lno, forwards_lno, current_lno;
/*
* If match_beginning or match_end is specified, there is no
if ((size_t) line > img->nr)
line = img->nr;
- try = 0;
+ current = 0;
for (i = 0; i < line; i++)
- try += img->line[i].len;
+ current += img->line[i].len;
/*
* There's probably some smart way to do this, but I'll leave
* that to the smart and beautiful people. I'm simple and stupid.
*/
- backwards = try;
+ backwards = current;
backwards_lno = line;
- forwards = try;
+ forwards = current;
forwards_lno = line;
- try_lno = line;
+ current_lno = line;
for (i = 0; ; i++) {
if (match_fragment(state, img, preimage, postimage,
- try, try_lno, ws_rule,
+ current, current_lno, ws_rule,
match_beginning, match_end))
- return try_lno;
+ return current_lno;
again:
if (backwards_lno == 0 && forwards_lno == img->nr)
}
backwards_lno--;
backwards -= img->line[backwards_lno].len;
- try = backwards;
- try_lno = backwards_lno;
+ current = backwards;
+ current_lno = backwards_lno;
} else {
if (forwards_lno == img->nr) {
i++;
}
forwards += img->line[forwards_lno].len;
forwards_lno++;
- try = forwards;
- try_lno = forwards_lno;
+ current = forwards;
+ current_lno = forwards_lno;
}
}
* See if the old one matches what the patch
* applies to.
*/
- hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
+ hash_object_file(img->buf, img->len, blob_type, &oid);
if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
return error(_("the patch applies to '%s' (%s), "
"which does not match the "
name);
/* verify that the result matches */
- hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
+ hash_object_file(img->buf, img->len, blob_type, &oid);
if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
name, patch->new_sha1_prefix, oid_to_hex(&oid));
/* Preimage the patch was prepared for */
if (patch->is_new)
- write_sha1_file("", 0, blob_type, pre_oid.hash);
+ write_object_file("", 0, blob_type, &pre_oid);
else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
read_blob_object(&buf, &pre_oid, patch->old_mode))
return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
return -1;
}
/* post_oid is theirs */
- write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_oid.hash);
+ write_object_file(tmp_image.buf, tmp_image.len, blob_type, &post_oid);
clear_image(&tmp_image);
/* our_oid is ours */
return error(_("cannot read the current contents of '%s'"),
patch->old_name);
}
- write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
+ write_object_file(tmp_image.buf, tmp_image.len, blob_type, &our_oid);
clear_image(&tmp_image);
/* in-core three-way merge between post and our using pre as base */
if (!patch->is_delete)
new_name = patch->new_name;
- if (old_name && !verify_path(old_name))
+ if (old_name && !verify_path(old_name, patch->old_mode))
return error(_("invalid path '%s'"), old_name);
- if (new_name && !verify_path(new_name))
+ if (new_name && !verify_path(new_name, patch->new_mode))
return error(_("invalid path '%s'"), new_name);
return 0;
}
static void show_rename_copy(struct patch *p)
{
const char *renamecopy = p->is_rename ? "rename" : "copy";
- const char *old, *new;
+ const char *old_name, *new_name;
/* Find common prefix */
- old = p->old_name;
- new = p->new_name;
+ old_name = p->old_name;
+ new_name = p->new_name;
while (1) {
const char *slash_old, *slash_new;
- slash_old = strchr(old, '/');
- slash_new = strchr(new, '/');
+ slash_old = strchr(old_name, '/');
+ slash_new = strchr(new_name, '/');
if (!slash_old ||
!slash_new ||
- slash_old - old != slash_new - new ||
- memcmp(old, new, slash_new - new))
+ slash_old - old_name != slash_new - new_name ||
+ memcmp(old_name, new_name, slash_new - new_name))
break;
- old = slash_old + 1;
- new = slash_new + 1;
+ old_name = slash_old + 1;
+ new_name = slash_new + 1;
}
- /* p->old_name thru old is the common prefix, and old and new
+ /* p->old_name thru old_name is the common prefix, and old_name and new_name
* through the end of names are renames
*/
- if (old != p->old_name)
+ if (old_name != p->old_name)
printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
- (int)(old - p->old_name), p->old_name,
- old, new, p->score);
+ (int)(old_name - p->old_name), p->old_name,
+ old_name, new_name, p->score);
else
printf(" %s %s => %s (%d%%)\n", renamecopy,
p->old_name, p->new_name, p->score);
}
fill_stat_cache_info(ce, &st);
}
- if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) {
+ if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
free(ce);
return error(_("unable to create backing store "
"for newly created file %s"), path);
N_("make sure the patch is applicable to the current index")),
OPT_BOOL(0, "cached", &state->cached,
N_("apply a patch without touching the working tree")),
- OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths,
- N_("accept a patch that touches outside the working area")),
+ OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
+ N_("accept a patch that touches outside the working area"),
+ PARSE_OPT_NOCOMPLETE),
OPT_BOOL(0, "apply", force_apply,
N_("also apply the patch (use with --stat/--summary/--check)")),
OPT_BOOL('3', "3way", &state->threeway,
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
#define OPT_RECURSIVE (1 << 2)
+#define OPT_FORCE (1 << 3)
typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
void *cb_data);
return ret;
}
+static int print_default_remote(int argc, const char **argv, const char *prefix)
+{
+ const char *remote;
+
+ if (argc != 1)
+ die(_("submodule--helper print-default-remote takes no arguments"));
+
+ remote = get_default_remote();
+ if (remote)
+ printf("%s\n", remote);
+
+ return 0;
+}
+
static int starts_with_dot_slash(const char *str)
{
return str[0] == '.' && is_dir_sep(str[1]);
*list = active_modules;
}
+static char *get_up_path(const char *path)
+{
+ int i;
+ struct strbuf sb = STRBUF_INIT;
+
+ for (i = count_slashes(path); i; i--)
+ strbuf_addstr(&sb, "../");
+
+ /*
+ * Check if 'path' ends with slash or not
+ * for having the same output for dir/sub_dir
+ * and dir/sub_dir/
+ */
+ if (!is_dir_sep(path[strlen(path) - 1]))
+ strbuf_addstr(&sb, "../");
+
+ return strbuf_detach(&sb, NULL);
+}
+
static int module_list(int argc, const char **argv, const char *prefix)
{
int i;
return 0;
}
+struct sync_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+
+#define SYNC_CB_INIT { NULL, 0 }
+
+static void sync_submodule(const char *path, const char *prefix,
+ unsigned int flags)
+{
+ const struct submodule *sub;
+ char *remote_key = NULL;
+ char *sub_origin_url, *super_config_url, *displaypath;
+ struct strbuf sb = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ char *sub_config_path = NULL;
+
+ if (!is_submodule_active(the_repository, path))
+ return;
+
+ sub = submodule_from_path(&null_oid, path);
+
+ if (sub && sub->url) {
+ if (starts_with_dot_dot_slash(sub->url) ||
+ starts_with_dot_slash(sub->url)) {
+ char *remote_url, *up_path;
+ char *remote = get_default_remote();
+ strbuf_addf(&sb, "remote.%s.url", remote);
+
+ if (git_config_get_string(sb.buf, &remote_url))
+ remote_url = xgetcwd();
+
+ up_path = get_up_path(path);
+ sub_origin_url = relative_url(remote_url, sub->url, up_path);
+ super_config_url = relative_url(remote_url, sub->url, NULL);
+
+ free(remote);
+ free(up_path);
+ free(remote_url);
+ } else {
+ sub_origin_url = xstrdup(sub->url);
+ super_config_url = xstrdup(sub->url);
+ }
+ } else {
+ sub_origin_url = xstrdup("");
+ super_config_url = xstrdup("");
+ }
+
+ displaypath = get_submodule_displaypath(path, prefix);
+
+ if (!(flags & OPT_QUIET))
+ printf(_("Synchronizing submodule url for '%s'\n"),
+ displaypath);
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "submodule.%s.url", sub->name);
+ if (git_config_set_gently(sb.buf, super_config_url))
+ die(_("failed to register url for submodule path '%s'"),
+ displaypath);
+
+ if (!is_submodule_populated_gently(path, NULL))
+ goto cleanup;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.dir = path;
+ argv_array_pushl(&cp.args, "submodule--helper",
+ "print-default-remote", NULL);
+
+ strbuf_reset(&sb);
+ if (capture_command(&cp, &sb, 0))
+ die(_("failed to get the default remote for submodule '%s'"),
+ path);
+
+ strbuf_strip_suffix(&sb, "\n");
+ remote_key = xstrfmt("remote.%s.url", sb.buf);
+
+ strbuf_reset(&sb);
+ submodule_to_gitdir(&sb, path);
+ strbuf_addstr(&sb, "/config");
+
+ if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+ die(_("failed to update remote for submodule '%s'"),
+ path);
+
+ if (flags & OPT_RECURSIVE) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = path;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_push(&cpr.args, "--super-prefix");
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
+ argv_array_pushl(&cpr.args, "submodule--helper", "sync",
+ "--recursive", NULL);
+
+ if (flags & OPT_QUIET)
+ argv_array_push(&cpr.args, "--quiet");
+
+ if (run_command(&cpr))
+ die(_("failed to recurse into submodule '%s'"),
+ path);
+ }
+
+cleanup:
+ free(super_config_url);
+ free(sub_origin_url);
+ strbuf_release(&sb);
+ free(remote_key);
+ free(displaypath);
+ free(sub_config_path);
+}
+
+static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+ struct sync_cb *info = cb_data;
+ sync_submodule(list_item->name, info->prefix, info->flags);
+
+}
+
+static int module_sync(int argc, const char **argv, const char *prefix)
+{
+ struct sync_cb info = SYNC_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int recursive = 0;
+
+ struct option module_sync_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
+ OPT_BOOL(0, "recursive", &recursive,
+ N_("Recurse into nested submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_sync_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+ if (recursive)
+ info.flags |= OPT_RECURSIVE;
+
+ for_each_listed_submodule(&list, sync_submodule_cb, &info);
+
+ return 0;
+}
+
+struct deinit_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+#define DEINIT_CB_INIT { NULL, 0 }
+
+static void deinit_submodule(const char *path, const char *prefix,
+ unsigned int flags)
+{
+ const struct submodule *sub;
+ char *displaypath = NULL;
+ struct child_process cp_config = CHILD_PROCESS_INIT;
+ struct strbuf sb_config = STRBUF_INIT;
+ char *sub_git_dir = xstrfmt("%s/.git", path);
+
+ sub = submodule_from_path(&null_oid, path);
+
+ if (!sub || !sub->name)
+ goto cleanup;
+
+ displaypath = get_submodule_displaypath(path, prefix);
+
+ /* remove the submodule work tree (unless the user already did it) */
+ if (is_directory(path)) {
+ struct strbuf sb_rm = STRBUF_INIT;
+ const char *format;
+
+ /*
+ * protect submodules containing a .git directory
+ * NEEDSWORK: instead of dying, automatically call
+ * absorbgitdirs and (possibly) warn.
+ */
+ if (is_directory(sub_git_dir))
+ die(_("Submodule work tree '%s' contains a .git "
+ "directory (use 'rm -rf' if you really want "
+ "to remove it including all of its history)"),
+ displaypath);
+
+ if (!(flags & OPT_FORCE)) {
+ struct child_process cp_rm = CHILD_PROCESS_INIT;
+ cp_rm.git_cmd = 1;
+ argv_array_pushl(&cp_rm.args, "rm", "-qn",
+ path, NULL);
+
+ if (run_command(&cp_rm))
+ die(_("Submodule work tree '%s' contains local "
+ "modifications; use '-f' to discard them"),
+ displaypath);
+ }
+
+ strbuf_addstr(&sb_rm, path);
+
+ if (!remove_dir_recursively(&sb_rm, 0))
+ format = _("Cleared directory '%s'\n");
+ else
+ format = _("Could not remove submodule work tree '%s'\n");
+
+ if (!(flags & OPT_QUIET))
+ printf(format, displaypath);
+
+ strbuf_release(&sb_rm);
+ }
+
+ if (mkdir(path, 0777))
+ printf(_("could not create empty submodule directory %s"),
+ displaypath);
+
+ cp_config.git_cmd = 1;
+ argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
+ argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
+
+ /* remove the .git/config entries (unless the user already did it) */
+ if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
+ char *sub_key = xstrfmt("submodule.%s", sub->name);
+ /*
+ * remove the whole section so we have a clean state when
+ * the user later decides to init this submodule again
+ */
+ git_config_rename_section_in_file(NULL, sub_key, NULL);
+ if (!(flags & OPT_QUIET))
+ printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
+ sub->name, sub->url, displaypath);
+ free(sub_key);
+ }
+
+cleanup:
+ free(displaypath);
+ free(sub_git_dir);
+ strbuf_release(&sb_config);
+}
+
+static void deinit_submodule_cb(const struct cache_entry *list_item,
+ void *cb_data)
+{
+ struct deinit_cb *info = cb_data;
+ deinit_submodule(list_item->name, info->prefix, info->flags);
+}
+
+static int module_deinit(int argc, const char **argv, const char *prefix)
+{
+ struct deinit_cb info = DEINIT_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+ int force = 0;
+ int all = 0;
+
+ struct option module_deinit_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+ OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
+ OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_deinit_options,
+ git_submodule_helper_usage, 0);
+
+ if (all && argc) {
+ error("pathspec and --all are incompatible");
+ usage_with_options(git_submodule_helper_usage,
+ module_deinit_options);
+ }
+
+ if (!argc && !all)
+ die(_("Use '--all' if you really want to deinitialize all submodules"));
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+ if (force)
+ info.flags |= OPT_FORCE;
+
+ for_each_listed_submodule(&list, deinit_submodule_cb, &info);
+
+ return 0;
+}
+
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, struct string_list *reference,
int quiet, int progress)
return !is_submodule_active(the_repository, argv[1]);
}
+ /*
+ * Exit non-zero if any of the submodule names given on the command line is
+ * invalid. If no names are given, filter stdin to print only valid names
+ * (which is primarily intended for testing).
+ */
+ static int check_name(int argc, const char **argv, const char *prefix)
+ {
+ if (argc > 1) {
+ while (*++argv) {
+ if (check_submodule_name(*argv) < 0)
+ return 1;
+ }
+ } else {
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!check_submodule_name(buf.buf))
+ printf("%s\n", buf.buf);
+ }
+ strbuf_release(&buf);
+ }
+ return 0;
+ }
+
#define SUPPORT_SUPER_PREFIX (1<<0)
struct cmd_struct {
{"resolve-relative-url-test", resolve_relative_url_test, 0},
{"init", module_init, SUPPORT_SUPER_PREFIX},
{"status", module_status, SUPPORT_SUPER_PREFIX},
+ {"print-default-remote", print_default_remote, 0},
+ {"sync", module_sync, SUPPORT_SUPER_PREFIX},
+ {"deinit", module_deinit, 0},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
{"is-active", is_active, 0},
+ {"check-name", check_name, 0},
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
return error("%s: is a directory - add files inside instead", path);
}
- static int process_path(const char *path)
+ static int process_path(const char *path, struct stat *st, int stat_errno)
{
int pos, len;
- struct stat st;
const struct cache_entry *ce;
len = strlen(path);
* First things first: get the stat information, to decide
* what to do about the pathname!
*/
- if (lstat(path, &st) < 0)
- return process_lstat_error(path, errno);
+ if (stat_errno)
+ return process_lstat_error(path, stat_errno);
- if (S_ISDIR(st.st_mode))
- return process_directory(path, len, &st);
+ if (S_ISDIR(st->st_mode))
+ return process_directory(path, len, st);
- return add_one_path(ce, path, len, &st);
+ return add_one_path(ce, path, len, st);
}
static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
int size, len, option;
struct cache_entry *ce;
- if (!verify_path(path))
+ if (!verify_path(path, mode))
return error("Invalid path '%s'", path);
len = strlen(path);
static void update_one(const char *path)
{
- if (!verify_path(path)) {
+ int stat_errno = 0;
+ struct stat st;
+
- if (mark_valid_only || mark_skip_worktree_only || force_remove)
++ if (mark_valid_only || mark_skip_worktree_only || force_remove ||
++ mark_fsmonitor_only)
+ st.st_mode = 0;
+ else if (lstat(path, &st) < 0) {
+ st.st_mode = 0;
+ stat_errno = errno;
+ } /* else stat is valid */
+
+ if (!verify_path(path, st.st_mode)) {
fprintf(stderr, "Ignoring path %s\n", path);
return;
}
report("remove '%s'", path);
return;
}
- if (process_path(path))
+ if (process_path(path, &st, stat_errno))
die("Unable to process path %s", path);
report("add '%s'", path);
}
path_name = uq.buf;
}
- if (!verify_path(path_name)) {
+ if (!verify_path(path_name, mode)) {
fprintf(stderr, "Ignoring path %s\n", path_name);
continue;
}
#include "git-compat-util.h"
#include "strbuf.h"
#include "hashmap.h"
-#include "mru.h"
+#include "list.h"
#include "advice.h"
#include "gettext.h"
#include "convert.h"
#include "sha1-array.h"
#include "repository.h"
-#ifndef platform_SHA_CTX
-/*
- * platform's underlying implementation of SHA-1; could be OpenSSL,
- * blk_SHA, Apple CommonCrypto, etc... Note that including
- * SHA1_HEADER may have already defined platform_SHA_CTX for our
- * own implementations like block-sha1 and ppc-sha1, so we list
- * the default for OpenSSL compatible SHA-1 implementations here.
- */
-#define platform_SHA_CTX SHA_CTX
-#define platform_SHA1_Init SHA1_Init
-#define platform_SHA1_Update SHA1_Update
-#define platform_SHA1_Final SHA1_Final
-#endif
-
-#define git_SHA_CTX platform_SHA_CTX
-#define git_SHA1_Init platform_SHA1_Init
-#define git_SHA1_Update platform_SHA1_Update
-#define git_SHA1_Final platform_SHA1_Final
-
-#ifdef SHA1_MAX_BLOCK_SIZE
-#include "compat/sha1-chunked.h"
-#undef git_SHA1_Update
-#define git_SHA1_Update git_SHA1_Update_Chunked
-#endif
-
#include <zlib.h>
typedef struct git_zstream {
z_stream z;
/* For use with `write_locked_index()`. */
#define COMMIT_LOCK (1 << 0)
+#define SKIP_IF_UNCHANGED (1 << 1)
/*
* Write the index while holding an already-taken lock. Close the lock,
* With `COMMIT_LOCK`, the lock is always committed or rolled back.
* Without it, the lock is closed, but neither committed nor rolled
* back.
+ *
+ * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
+ * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
*/
extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
*/
extern int index_has_changes(struct strbuf *sb);
- extern int verify_path(const char *path);
+ extern int verify_path(const char *path, unsigned mode);
extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
extern void adjust_dirname_case(struct index_state *istate, char *name);
#define GIT_REPO_VERSION 0
#define GIT_REPO_VERSION_READ 1
extern int repository_format_precious_objects;
+extern char *repository_format_partial_clone;
+extern const char *core_partial_clone_filter_default;
struct repository_format {
int version;
int precious_objects;
+ char *partial_clone; /* value of extensions.partialclone */
int is_bare;
int hash_algo;
char *work_tree;
#define TYPE_CHANGED 0x0040
/*
- * Return the name of the file in the local object database that would
- * be used to store a loose object with the specified sha1. The
- * return value is a pointer to a statically allocated buffer that is
- * overwritten each time the function is called.
+ * Put in `buf` the name of the file in the local object database that
+ * would be used to store a loose object with the specified sha1.
*/
-extern const char *sha1_file_name(const unsigned char *sha1);
+extern void sha1_file_name(struct strbuf *buf, const unsigned char *sha1);
/*
* Return an abbreviated sha1 unique within this repository's object database.
static inline void oidclr(struct object_id *oid)
{
- hashclr(oid->hash);
+ memset(oid->hash, 0, GIT_MAX_RAWSZ);
}
"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
"\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
extern const struct object_id empty_blob_oid;
-#define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash)
-
static inline int is_empty_blob_sha1(const unsigned char *sha1)
{
int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
int daemon_avoid_alias(const char *path);
- extern int is_ntfs_dotgit(const char *name);
+
+ /*
+ * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
+ * details.
+ */
+ int is_ntfs_dotgit(const char *name);
+ int is_ntfs_dotgitmodules(const char *name);
+ int is_ntfs_dotgitignore(const char *name);
+ int is_ntfs_dotgitattributes(const char *name);
/*
* Returns true iff "str" could be confused as a command-line option when
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
-extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
-extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
-extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, struct object_id *oid, unsigned flags);
-extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
-extern int force_object_loose(const unsigned char *sha1, time_t mtime);
+
+extern int hash_object_file(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid);
+
+extern int write_object_file(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid);
+
+extern int hash_object_file_literally(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid,
+ unsigned flags);
+
+extern int pretend_object_file(void *, unsigned long, enum object_type,
+ struct object_id *oid);
+
+extern int force_object_loose(const struct object_id *oid, time_t mtime);
+
extern int git_open_cloexec(const char *name, int flags);
#define git_open(name) git_open_cloexec(name, O_RDONLY)
extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
extern struct packed_git {
struct packed_git *next;
+ struct list_head mru;
struct pack_window *windows;
off_t pack_size;
const void *index_data;
unsigned pack_local:1,
pack_keep:1,
freshened:1,
- do_not_close:1;
+ do_not_close:1,
+ pack_promisor:1;
unsigned char sha1[20];
struct revindex_entry *revindex;
/* something like ".git/objects/pack/xxxxx.pack" */
} *packed_git;
/*
- * A most-recently-used ordered version of the packed_git list, which can
- * be iterated instead of packed_git (and marked via mru_mark).
+ * A most-recently-used ordered version of the packed_git list.
*/
-extern struct mru packed_git_mru;
+extern struct list_head packed_git_mru;
struct pack_entry {
off_t offset;
* usual "XXXXXX" trailer, and the resulting filename is written into the
* "template" buffer. Returns the open descriptor.
*/
-extern int odb_mkstemp(struct strbuf *template, const char *pattern);
+extern int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
/*
* Create a pack .keep file named "name" (which should generally be the output
unsigned long *sizep;
off_t *disk_sizep;
unsigned char *delta_base_sha1;
- struct strbuf *typename;
+ struct strbuf *type_name;
void **contentp;
/* Response */
#define OBJECT_INFO_QUICK 8
extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
+/*
+ * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
+ * blobs. This has a difference only if extensions.partialClone is set.
+ *
+ * Its default value is 1.
+ */
+extern int fetch_if_missing;
+
/* Dumb servers support */
extern int update_server_info(int);
* 1 along with { data, size } of the (possibly augmented) buffer
* when successful.
*
- * Optionally updates the given sha1_stat with the given OID (when valid).
+ * Optionally updates the given oid_stat with the given OID (when valid).
*/
-static int do_read_blob(const struct object_id *oid,
- struct sha1_stat *sha1_stat,
- size_t *size_out,
- char **data_out)
+static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
+ size_t *size_out, char **data_out)
{
enum object_type type;
unsigned long sz;
return -1;
}
- if (sha1_stat) {
- memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
- hashcpy(sha1_stat->sha1, oid->hash);
+ if (oid_stat) {
+ memset(&oid_stat->stat, 0, sizeof(oid_stat->stat));
+ oidcpy(&oid_stat->oid, oid);
}
if (sz == 0) {
static int read_skip_worktree_file_from_index(const struct index_state *istate,
const char *path,
- size_t *size_out,
- char **data_out,
- struct sha1_stat *sha1_stat)
+ size_t *size_out, char **data_out,
+ struct oid_stat *oid_stat)
{
int pos, len;
if (!ce_skip_worktree(istate->cache[pos]))
return -1;
- return do_read_blob(&istate->cache[pos]->oid, sha1_stat, size_out, data_out);
+ return do_read_blob(&istate->cache[pos]->oid, oid_stat, size_out, data_out);
}
/*
FLEX_ALLOC_MEM(d, name, name, len);
ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
- memmove(dir->dirs + first + 1, dir->dirs + first,
- (dir->dirs_nr - first) * sizeof(*dir->dirs));
+ MOVE_ARRAY(dir->dirs + first + 1, dir->dirs + first,
+ dir->dirs_nr - first);
dir->dirs_nr++;
dir->dirs[first] = d;
return d;
struct untracked_cache_dir *dir)
{
int i;
- uc->dir_invalidated++;
+
+ /*
+ * Invalidation increment here is just roughly correct. If
+ * untracked_nr or any of dirs[].recurse is non-zero, we
+ * should increment dir_invalidated too. But that's more
+ * expensive to do.
+ */
+ if (dir->valid)
+ uc->dir_invalidated++;
+
dir->valid = 0;
dir->untracked_nr = 0;
for (i = 0; i < dir->dirs_nr; i++)
* ss_valid is non-zero, "ss" must contain good value as input.
*/
static int add_excludes(const char *fname, const char *base, int baselen,
- struct exclude_list *el,
- struct index_state *istate,
- struct sha1_stat *sha1_stat)
+ struct exclude_list *el, struct index_state *istate,
+ struct oid_stat *oid_stat)
{
struct stat st;
int r;
return -1;
r = read_skip_worktree_file_from_index(istate, fname,
&size, &buf,
- sha1_stat);
+ oid_stat);
if (r != 1)
return r;
} else {
size = xsize_t(st.st_size);
if (size == 0) {
- if (sha1_stat) {
- fill_stat_data(&sha1_stat->stat, &st);
- hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
- sha1_stat->valid = 1;
+ if (oid_stat) {
+ fill_stat_data(&oid_stat->stat, &st);
+ oidcpy(&oid_stat->oid, &empty_blob_oid);
+ oid_stat->valid = 1;
}
close(fd);
return 0;
}
buf[size++] = '\n';
close(fd);
- if (sha1_stat) {
+ if (oid_stat) {
int pos;
- if (sha1_stat->valid &&
- !match_stat_data_racy(istate, &sha1_stat->stat, &st))
+ if (oid_stat->valid &&
+ !match_stat_data_racy(istate, &oid_stat->stat, &st))
; /* no content change, ss->sha1 still good */
else if (istate &&
(pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
!ce_stage(istate->cache[pos]) &&
ce_uptodate(istate->cache[pos]) &&
!would_convert_to_git(istate, fname))
- hashcpy(sha1_stat->sha1,
- istate->cache[pos]->oid.hash);
+ oidcpy(&oid_stat->oid,
+ &istate->cache[pos]->oid);
else
- hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
- fill_stat_data(&sha1_stat->stat, &st);
- sha1_stat->valid = 1;
+ hash_object_file(buf, size, "blob",
+ &oid_stat->oid);
+ fill_stat_data(&oid_stat->stat, &st);
+ oid_stat->valid = 1;
}
}
* Used to set up core.excludesfile and .git/info/exclude lists.
*/
static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
- struct sha1_stat *sha1_stat)
+ struct oid_stat *oid_stat)
{
struct exclude_list *el;
/*
if (!dir->untracked)
dir->unmanaged_exclude_files++;
el = add_exclude_list(dir, EXC_FILE, fname);
- if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0)
+ if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
die("cannot use %s as an exclude file", fname);
}
while (current < baselen) {
const char *cp;
- struct sha1_stat sha1_stat;
+ struct oid_stat oid_stat;
stk = xcalloc(1, sizeof(*stk));
if (current < 0) {
}
/* Try to read per-directory file */
- hashclr(sha1_stat.sha1);
- sha1_stat.valid = 0;
+ oidclr(&oid_stat.oid);
+ oid_stat.valid = 0;
if (dir->exclude_per_dir &&
/*
* If we know that no files have been added in
strbuf_addstr(&sb, dir->exclude_per_dir);
el->src = strbuf_detach(&sb, NULL);
add_excludes(el->src, el->src, stk->baselen, el, istate,
- untracked ? &sha1_stat : NULL);
+ untracked ? &oid_stat : NULL);
}
/*
* NEEDSWORK: when untracked cache is enabled, prep_exclude()
* order, though, if you do that.
*/
if (untracked &&
- hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
+ hashcmp(oid_stat.oid.hash, untracked->exclude_sha1)) {
invalidate_gitignore(dir->untracked, untracked);
- hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
+ hashcpy(untracked->exclude_sha1, oid_stat.oid.hash);
}
dir->exclude_stack = stk;
current = stk->baselen;
if (!de)
return treat_path_fast(dir, untracked, cdir, istate, path,
baselen, pathspec);
- if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
+ if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
return path_none;
strbuf_setlen(path, baselen);
strbuf_addstr(path, de->d_name);
*/
refresh_fsmonitor(istate);
if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
- if (stat(path->len ? path->buf : ".", &st)) {
- invalidate_directory(dir->untracked, untracked);
+ if (lstat(path->len ? path->buf : ".", &st)) {
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
return 0;
}
if (!untracked->valid ||
match_stat_data_racy(istate, &untracked->stat_data, &st)) {
- if (untracked->valid)
- invalidate_directory(dir->untracked, untracked);
fill_stat_data(&untracked->stat_data, &st);
return 0;
}
}
- if (untracked->check_only != !!check_only) {
- invalidate_directory(dir->untracked, untracked);
+ if (untracked->check_only != !!check_only)
return 0;
- }
/*
* prep_exclude will be called eventually on this directory,
struct strbuf *path,
int check_only)
{
+ const char *c_path;
+
memset(cdir, 0, sizeof(*cdir));
cdir->untracked = untracked;
if (valid_cached_dir(dir, untracked, istate, path, check_only))
return 0;
- cdir->fdir = opendir(path->len ? path->buf : ".");
- if (dir->untracked)
+ c_path = path->len ? path->buf : ".";
+ cdir->fdir = opendir(c_path);
+ if (!cdir->fdir)
+ warning_errno(_("could not open directory '%s'"), c_path);
+ if (dir->untracked) {
+ invalidate_directory(dir->untracked, untracked);
dir->untracked->dir_opened++;
+ }
if (!cdir->fdir)
return -1;
return 0;
const struct pathspec *pathspec)
{
struct untracked_cache_dir *root;
+ static int untracked_cache_disabled = -1;
- if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
+ if (!dir->untracked)
+ return NULL;
+ if (untracked_cache_disabled < 0)
+ untracked_cache_disabled = git_env_bool("GIT_DISABLE_UNTRACKED_CACHE", 0);
+ if (untracked_cache_disabled)
return NULL;
/*
/* Validate $GIT_DIR/info/exclude and core.excludesfile */
root = dir->untracked->root;
- if (hashcmp(dir->ss_info_exclude.sha1,
- dir->untracked->ss_info_exclude.sha1)) {
+ if (oidcmp(&dir->ss_info_exclude.oid,
+ &dir->untracked->ss_info_exclude.oid)) {
invalidate_gitignore(dir->untracked, root);
dir->untracked->ss_info_exclude = dir->ss_info_exclude;
}
- if (hashcmp(dir->ss_excludes_file.sha1,
- dir->untracked->ss_excludes_file.sha1)) {
+ if (oidcmp(&dir->ss_excludes_file.oid,
+ &dir->untracked->ss_excludes_file.oid)) {
invalidate_gitignore(dir->untracked, root);
dir->untracked->ss_excludes_file = dir->ss_excludes_file;
}
const char *path, int len, const struct pathspec *pathspec)
{
struct untracked_cache_dir *untracked;
+ uint64_t start = getnanotime();
if (has_symlink_leading_path(path, len))
return dir->nr;
dir->nr = i;
}
+ trace_performance_since(start, "read directory %.*s", len, path);
if (dir->untracked) {
+ static int force_untracked_cache = -1;
static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
+
+ if (force_untracked_cache < 0)
+ force_untracked_cache =
+ git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
trace_printf_key(&trace_untracked_stats,
"node creation: %u\n"
"gitignore invalidation: %u\n"
dir->untracked->gitignore_invalidated,
dir->untracked->dir_invalidated,
dir->untracked->dir_opened);
- if (dir->untracked == istate->untracked &&
+ if (force_untracked_cache &&
+ dir->untracked == istate->untracked &&
(dir->untracked->dir_opened ||
dir->untracked->gitignore_invalidated ||
dir->untracked->dir_invalidated))
FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
- hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
- hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
+ hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash);
+ hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
ouc->dir_flags = htonl(untracked->dir_flags);
varint_len = encode_varint(untracked->ident.len, varbuf);
rd->data += 20;
}
-static void load_sha1_stat(struct sha1_stat *sha1_stat,
- const unsigned char *data,
- const unsigned char *sha1)
+static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
+ const unsigned char *sha1)
{
- stat_data_from_disk(&sha1_stat->stat, data);
- hashcpy(sha1_stat->sha1, sha1);
- sha1_stat->valid = 1;
+ stat_data_from_disk(&oid_stat->stat, data);
+ hashcpy(oid_stat->oid.hash, sha1);
+ oid_stat->valid = 1;
}
struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
uc = xcalloc(1, sizeof(*uc));
strbuf_init(&uc->ident, ident_len);
strbuf_add(&uc->ident, ident, ident_len);
- load_sha1_stat(&uc->ss_info_exclude,
- next + ouc_offset(info_exclude_stat),
- next + ouc_offset(info_exclude_sha1));
- load_sha1_stat(&uc->ss_excludes_file,
- next + ouc_offset(excludes_file_stat),
- next + ouc_offset(excludes_file_sha1));
+ load_oid_stat(&uc->ss_info_exclude,
+ next + ouc_offset(info_exclude_stat),
+ next + ouc_offset(info_exclude_sha1));
+ load_oid_stat(&uc->ss_excludes_file,
+ next + ouc_offset(excludes_file_stat),
+ next + ouc_offset(excludes_file_sha1));
uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
uc->exclude_per_dir = xstrdup(exclude_per_dir);
}
void untracked_cache_invalidate_path(struct index_state *istate,
- const char *path)
+ const char *path, int safe_path)
{
if (!istate->untracked || !istate->untracked->root)
return;
- if (!safe_path && !verify_path(path))
++ if (!safe_path && !verify_path(path, 0))
+ return;
invalidate_one_component(istate->untracked, istate->untracked->root,
path, strlen(path));
}
void untracked_cache_remove_from_index(struct index_state *istate,
const char *path)
{
- untracked_cache_invalidate_path(istate, path);
+ untracked_cache_invalidate_path(istate, path, 1);
}
void untracked_cache_add_to_index(struct index_state *istate,
const char *path)
{
- untracked_cache_invalidate_path(istate, path);
+ untracked_cache_invalidate_path(istate, path, 1);
}
/* Update gitfile and core.worktree setting to connect work tree and git dir */
extern int xdup(int fd);
extern FILE *xfopen(const char *path, const char *mode);
extern FILE *xfdopen(int fd, const char *mode);
-extern int xmkstemp(char *template);
-extern int xmkstemp_mode(char *template, int mode);
+extern int xmkstemp(char *temp_filename);
+extern int xmkstemp_mode(char *temp_filename, int mode);
extern char *xgetcwd(void);
extern FILE *fopen_for_writing(const char *path);
extern FILE *fopen_or_warn(const char *path, const char *mode);
return (x & 0x20) == 0;
}
+ /*
+ * Like skip_prefix, but compare case-insensitively. Note that the comparison
+ * is done via tolower(), so it is strictly ASCII (no multi-byte characters or
+ * locale-specific conversions).
+ */
+ static inline int skip_iprefix(const char *str, const char *prefix,
+ const char **out)
+ {
+ do {
+ if (!*prefix) {
+ *out = str;
+ return 1;
+ }
+ } while (tolower(*str++) == tolower(*prefix++));
+ return 0;
+ }
+
static inline int strtoul_ui(char const *s, int base, unsigned int *result)
{
unsigned long ul;
sm_name="$sm_path"
fi
+ if ! git submodule--helper check-name "$sm_name"
+ then
+ die "$(eval_gettext "'$sm_name' is not a valid submodule name")"
+ fi
+
# perhaps the path exists and is already a git repo, else clone it
if test -e "$sm_path"
then
shift
done
- if test -n "$deinit_all" && test "$#" -ne 0
- then
- echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
- usage
- fi
- if test $# = 0 && test -z "$deinit_all"
- then
- die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
- fi
-
- {
- git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched" $?
- } |
- while read -r mode sha1 stage sm_path
- do
- die_if_unmatched "$mode" "$sha1"
- name=$(git submodule--helper name "$sm_path") || exit
-
- displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
-
- # Remove the submodule work tree (unless the user already did it)
- if test -d "$sm_path"
- then
- # Protect submodules containing a .git directory
- if test -d "$sm_path/.git"
- then
- die "$(eval_gettext "\
-Submodule work tree '\$displaypath' contains a .git directory
-(use 'rm -rf' if you really want to remove it including all of its history)")"
- fi
-
- if test -z "$force"
- then
- git rm -qn "$sm_path" ||
- die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
- fi
- rm -rf "$sm_path" &&
- say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
- say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
- fi
-
- mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
-
- # Remove the .git/config entries (unless the user already did it)
- if test -n "$(git config --get-regexp submodule."$name\.")"
- then
- # Remove the whole section so we have a clean state when
- # the user later decides to init this submodule again
- url=$(git config submodule."$name".url)
- git config --remove-section submodule."$name" 2>/dev/null &&
- say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
- fi
- done
+ git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
}
is_tip_reachable () (
;;
esac
done
- cd_to_toplevel
- {
- git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched" $?
- } |
- while read -r mode sha1 stage sm_path
- do
- die_if_unmatched "$mode" "$sha1"
-
- # skip inactive submodules
- if ! git submodule--helper is-active "$sm_path"
- then
- continue
- fi
-
- name=$(git submodule--helper name "$sm_path")
- url=$(git config -f .gitmodules --get submodule."$name".url)
-
- # Possibly a url relative to parent
- case "$url" in
- ./*|../*)
- # rewrite foo/bar as ../.. to find path from
- # submodule work tree to superproject work tree
- up_path="$(printf '%s\n' "$sm_path" | sed "s/[^/][^/]*/../g")" &&
- # guarantee a trailing /
- up_path=${up_path%/}/ &&
- # path from submodule work tree to submodule origin repo
- sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
- # path from superproject work tree to submodule origin repo
- super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
- ;;
- *)
- sub_origin_url="$url"
- super_config_url="$url"
- ;;
- esac
-
- displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
- say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
- git config submodule."$name".url "$super_config_url"
-
- if test -e "$sm_path"/.git
- then
- (
- sanitize_submodule_env
- cd "$sm_path"
- remote=$(get_default_remote)
- git config remote."$remote".url "$sub_origin_url"
- if test -n "$recursive"
- then
- prefix="$prefix$sm_path/"
- eval cmd_sync
- fi
- )
- fi
- done
+ git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
}
cmd_absorbgitdirs()
replace_index_entry_in_base(istate, old, ce);
remove_name_hash(istate, old);
free(old);
+ ce->ce_flags &= ~CE_HASHED;
set_index_entry(istate, nr, ce);
ce->ce_flags |= CE_UPDATE_IN_BASE;
mark_fsmonitor_invalid(istate, ce);
void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
{
- struct cache_entry *old = istate->cache[nr], *new;
+ struct cache_entry *old_entry = istate->cache[nr], *new_entry;
int namelen = strlen(new_name);
- new = xmalloc(cache_entry_size(namelen));
- copy_cache_entry(new, old);
- new->ce_flags &= ~CE_HASHED;
- new->ce_namelen = namelen;
- new->index = 0;
- memcpy(new->name, new_name, namelen + 1);
+ new_entry = xmalloc(cache_entry_size(namelen));
+ copy_cache_entry(new_entry, old_entry);
+ new_entry->ce_flags &= ~CE_HASHED;
+ new_entry->ce_namelen = namelen;
+ new_entry->index = 0;
+ memcpy(new_entry->name, new_name, namelen + 1);
- cache_tree_invalidate_path(istate, old->name);
- untracked_cache_remove_from_index(istate, old->name);
+ cache_tree_invalidate_path(istate, old_entry->name);
+ untracked_cache_remove_from_index(istate, old_entry->name);
remove_index_entry_at(istate, nr);
- add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+ add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
}
void fill_stat_data(struct stat_data *sd, struct stat *st)
struct cache_entry *alias)
{
int len;
- struct cache_entry *new;
+ struct cache_entry *new_entry;
if (alias->ce_flags & CE_ADDED)
die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name);
/* Ok, create the new entry using the name of the existing alias */
len = ce_namelen(alias);
- new = xcalloc(1, cache_entry_size(len));
- memcpy(new->name, alias->name, len);
- copy_cache_entry(new, ce);
+ new_entry = xcalloc(1, cache_entry_size(len));
+ memcpy(new_entry->name, alias->name, len);
+ copy_cache_entry(new_entry, ce);
save_or_free_index_entry(istate, ce);
- return new;
+ return new_entry;
}
void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
{
- unsigned char sha1[20];
- if (write_sha1_file("", 0, blob_type, sha1))
+ struct object_id oid;
+ if (write_object_file("", 0, blob_type, &oid))
die("cannot create an empty blob in the object database");
- hashcpy(ce->oid.hash, sha1);
+ oidcpy(&ce->oid, &oid);
}
int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
int size, len;
struct cache_entry *ce, *ret;
- if (!verify_path(path)) {
+ if (!verify_path(path, mode)) {
error("Invalid path '%s'", path);
return NULL;
}
* Also, we don't want double slashes or slashes at the
* end that can make pathnames ambiguous.
*/
- static int verify_dotfile(const char *rest)
+ static int verify_dotfile(const char *rest, unsigned mode)
{
/*
* The first character was '.', but that
switch (*rest) {
/*
- * ".git" followed by NUL or slash is bad. This
- * shares the path end test with the ".." case.
+ * ".git" followed by NUL or slash is bad. Note that we match
+ * case-insensitively here, even if ignore_case is not set.
+ * This outlaws ".GIT" everywhere out of an abundance of caution,
+ * since there's really no good reason to allow it.
+ *
+ * Once we've seen ".git", we can also find ".gitmodules", etc (also
+ * case-insensitively).
*/
case 'g':
case 'G':
break;
if (rest[2] != 't' && rest[2] != 'T')
break;
- rest += 2;
- /* fallthrough */
+ if (rest[3] == '\0' || is_dir_sep(rest[3]))
+ return 0;
+ if (S_ISLNK(mode)) {
+ rest += 3;
+ if (skip_iprefix(rest, "modules", &rest) &&
+ (*rest == '\0' || is_dir_sep(*rest)))
+ return 0;
+ }
+ break;
case '.':
if (rest[1] == '\0' || is_dir_sep(rest[1]))
return 0;
return 1;
}
- int verify_path(const char *path)
+ int verify_path(const char *path, unsigned mode)
{
char c;
return 1;
if (is_dir_sep(c)) {
inside:
- if (protect_hfs && is_hfs_dotgit(path))
- return 0;
- if (protect_ntfs && is_ntfs_dotgit(path))
- return 0;
+ if (protect_hfs) {
+ if (is_hfs_dotgit(path))
+ return 0;
+ if (S_ISLNK(mode)) {
+ if (is_hfs_dotgitmodules(path))
+ return 0;
+ }
+ }
+ if (protect_ntfs) {
+ if (is_ntfs_dotgit(path))
+ return 0;
+ if (S_ISLNK(mode)) {
+ if (is_ntfs_dotgitmodules(path))
+ return 0;
+ }
+ }
+
c = *path++;
- if ((c == '.' && !verify_dotfile(path)) ||
+ if ((c == '.' && !verify_dotfile(path, mode)) ||
is_dir_sep(c) || c == '\0')
return 0;
}
if (!ok_to_add)
return -1;
- if (!verify_path(ce->name))
+ if (!verify_path(ce->name, ce->ce_mode))
return error("Invalid path '%s'", ce->name);
if (!skip_df_check &&
/* Add it in.. */
istate->cache_nr++;
if (istate->cache_nr > pos + 1)
- memmove(istate->cache + pos + 1,
- istate->cache + pos,
- (istate->cache_nr - pos - 1) * sizeof(ce));
+ MOVE_ARRAY(istate->cache + pos + 1, istate->cache + pos,
+ istate->cache_nr - pos - 1);
set_index_entry(istate, pos, ce);
istate->cache_changed |= CE_ENTRY_ADDED;
return 0;
size = ce_size(ce);
updated = xmalloc(size);
- memcpy(updated, ce, size);
+ copy_cache_entry(updated, ce);
+ memcpy(updated->name, ce->name, ce->ce_namelen + 1);
fill_stat_cache_info(updated, &st);
/*
* If ignore_valid is not set, we should leave CE_VALID bit
const char *typechange_fmt;
const char *added_fmt;
const char *unmerged_fmt;
+ uint64_t start = getnanotime();
modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n");
unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce, *new;
+ struct cache_entry *ce, *new_entry;
int cache_errno = 0;
int changed = 0;
int filtered = 0;
if (filtered)
continue;
- new = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
- if (new == ce)
+ new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
+ if (new_entry == ce)
continue;
- if (!new) {
+ if (!new_entry) {
const char *fmt;
if (really && cache_errno == EINVAL) {
continue;
}
- replace_index_entry(istate, i, new);
+ replace_index_entry(istate, i, new_entry);
}
+ trace_performance_since(start, "refresh index");
return has_errors;
}
static int verify_hdr(struct cache_header *hdr, unsigned long size)
{
- git_SHA_CTX c;
- unsigned char sha1[20];
+ git_hash_ctx c;
+ unsigned char hash[GIT_MAX_RAWSZ];
int hdr_version;
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
if (!verify_index_checksum)
return 0;
- git_SHA1_Init(&c);
- git_SHA1_Update(&c, hdr, size - 20);
- git_SHA1_Final(sha1, &c);
- if (hashcmp(sha1, (unsigned char *)hdr + size - 20))
+ the_hash_algo->init_fn(&c);
+ the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
+ the_hash_algo->final_fn(hash, &c);
+ if (hashcmp(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
return error("bad index file sha1 signature");
return 0;
}
die_errno("cannot stat the open index");
mmap_size = xsize_t(st.st_size);
- if (mmap_size < sizeof(struct cache_header) + 20)
+ if (mmap_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
die("index file smaller than expected");
mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (verify_hdr(hdr, mmap_size) < 0)
goto unmap;
- hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - 20);
+ hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);
istate->version = ntohl(hdr->hdr_version);
istate->cache_nr = ntohl(hdr->hdr_entries);
istate->cache_alloc = alloc_nr(istate->cache_nr);
istate->timestamp.sec = st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
- while (src_offset <= mmap_size - 20 - 8) {
+ while (src_offset <= mmap_size - the_hash_algo->rawsz - 8) {
/* After an array of active_nr index entries,
* there can be arbitrary number of extended
* sections, each of which is prefixed with
int read_index_from(struct index_state *istate, const char *path,
const char *gitdir)
{
+ uint64_t start = getnanotime();
struct split_index *split_index;
int ret;
char *base_sha1_hex;
return istate->cache_nr;
ret = do_read_index(istate, path, 0);
+ trace_performance_since(start, "read cache %s", path);
split_index = istate->split_index;
if (!split_index || is_null_sha1(split_index->base_sha1)) {
freshen_shared_index(base_path, 0);
merge_base_index(istate);
post_read_index_from(istate);
+ trace_performance_since(start, "read cache %s", base_path);
free(base_path);
return ret;
}
static unsigned char write_buffer[WRITE_BUFFER_SIZE];
static unsigned long write_buffer_len;
-static int ce_write_flush(git_SHA_CTX *context, int fd)
+static int ce_write_flush(git_hash_ctx *context, int fd)
{
unsigned int buffered = write_buffer_len;
if (buffered) {
- git_SHA1_Update(context, write_buffer, buffered);
+ the_hash_algo->update_fn(context, write_buffer, buffered);
if (write_in_full(fd, write_buffer, buffered) < 0)
return -1;
write_buffer_len = 0;
return 0;
}
-static int ce_write(git_SHA_CTX *context, int fd, void *data, unsigned int len)
+static int ce_write(git_hash_ctx *context, int fd, void *data, unsigned int len)
{
while (len) {
unsigned int buffered = write_buffer_len;
return 0;
}
-static int write_index_ext_header(git_SHA_CTX *context, int fd,
+static int write_index_ext_header(git_hash_ctx *context, int fd,
unsigned int ext, unsigned int sz)
{
ext = htonl(ext);
(ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
}
-static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
+static int ce_flush(git_hash_ctx *context, int fd, unsigned char *hash)
{
unsigned int left = write_buffer_len;
if (left) {
write_buffer_len = 0;
- git_SHA1_Update(context, write_buffer, left);
+ the_hash_algo->update_fn(context, write_buffer, left);
}
- /* Flush first if not enough space for SHA1 signature */
- if (left + 20 > WRITE_BUFFER_SIZE) {
+ /* Flush first if not enough space for hash signature */
+ if (left + the_hash_algo->rawsz > WRITE_BUFFER_SIZE) {
if (write_in_full(fd, write_buffer, left) < 0)
return -1;
left = 0;
}
- /* Append the SHA1 signature at the end */
- git_SHA1_Final(write_buffer + left, context);
- hashcpy(sha1, write_buffer + left);
- left += 20;
+ /* Append the hash signature at the end */
+ the_hash_algo->final_fn(write_buffer + left, context);
+ hashcpy(hash, write_buffer + left);
+ left += the_hash_algo->rawsz;
return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
}
}
}
-static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
+static int ce_write_entry(git_hash_ctx *c, int fd, struct cache_entry *ce,
struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
{
int size;
- int saved_namelen = saved_namelen; /* compiler workaround */
int result;
+ unsigned int saved_namelen;
+ int stripped_name = 0;
static unsigned char padding[8] = { 0x00 };
if (ce->ce_flags & CE_STRIP_NAME) {
saved_namelen = ce_namelen(ce);
ce->ce_namelen = 0;
+ stripped_name = 1;
}
if (ce->ce_flags & CE_EXTENDED)
strbuf_splice(previous_name, common, to_remove,
ce->name + common, ce_namelen(ce) - common);
}
- if (ce->ce_flags & CE_STRIP_NAME) {
+ if (stripped_name) {
ce->ce_namelen = saved_namelen;
ce->ce_flags &= ~CE_STRIP_NAME;
}
int fd;
ssize_t n;
struct stat st;
- unsigned char sha1[20];
+ unsigned char hash[GIT_MAX_RAWSZ];
if (!istate->initialized)
return 0;
if (fstat(fd, &st))
goto out;
- if (st.st_size < sizeof(struct cache_header) + 20)
+ if (st.st_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
goto out;
- n = pread_in_full(fd, sha1, 20, st.st_size - 20);
- if (n != 20)
+ n = pread_in_full(fd, hash, the_hash_algo->rawsz, st.st_size - the_hash_algo->rawsz);
+ if (n != the_hash_algo->rawsz)
goto out;
- if (hashcmp(istate->sha1, sha1))
+ if (hashcmp(istate->sha1, hash))
goto out;
close(fd);
static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
int strip_extensions)
{
+ uint64_t start = getnanotime();
int newfd = tempfile->fd;
- git_SHA_CTX c;
+ git_hash_ctx c;
struct cache_header hdr;
int i, err = 0, removed, extended, hdr_version;
struct cache_entry **cache = istate->cache;
hdr.hdr_version = htonl(hdr_version);
hdr.hdr_entries = htonl(entries - removed);
- git_SHA1_Init(&c);
+ the_hash_algo->init_fn(&c);
if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
return -1;
return -1;
istate->timestamp.sec = (unsigned int)st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
+ trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
return 0;
}
int new_shared_index, ret;
struct split_index *si = istate->split_index;
+ if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
+ if (flags & COMMIT_LOCK)
+ rollback_lock_file(lock);
+ return 0;
+ }
+
if (istate->fsmonitor_last_update)
fill_fsmonitor_bitmap(istate);