]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/update-index.c
sparse-index.h: move declarations for sparse-index.c from cache.h
[thirdparty/git.git] / builtin / update-index.c
index f1f16f2de526d907cc8c5b41da64a7eef3d22a7d..47dc9b626ef334da8daaf6731517f23067684ff0 100644 (file)
@@ -3,21 +3,32 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "cache.h"
+#include "bulk-checkin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
 #include "builtin.h"
+#include "object-file.h"
 #include "refs.h"
 #include "resolve-undo.h"
 #include "parse-options.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "repository.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "split-index.h"
+#include "symlinks.h"
 #include "fsmonitor.h"
+#include "write-or-die.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -57,6 +68,14 @@ static void report(const char *fmt, ...)
        if (!verbose)
                return;
 
+       /*
+        * It is possible, though unlikely, that a caller could use the verbose
+        * output to synchronize with addition of objects to the object
+        * database. The current implementation of ODB transactions leaves
+        * objects invisible while a transaction is active, so flush the
+        * transaction here before reporting a change made by update-index.
+        */
+       flush_odb_transaction();
        va_start(vp, fmt);
        vprintf(fmt, vp);
        putchar('\n');
@@ -95,9 +114,7 @@ static int create_file(const char *path)
 {
        int fd;
        path = get_mtime_path(path);
-       fd = open(path, O_CREAT | O_RDWR, 0644);
-       if (fd < 0)
-               die_errno(_("failed to create file %s"), path);
+       fd = xopen(path, O_CREAT | O_RDWR, 0644);
        return fd;
 }
 
@@ -230,16 +247,16 @@ done:
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
        int namelen = strlen(path);
-       int pos = cache_name_pos(path, namelen);
+       int pos = index_name_pos(&the_index, path, namelen);
        if (0 <= pos) {
-               mark_fsmonitor_invalid(&the_index, active_cache[pos]);
+               mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
                if (mark)
-                       active_cache[pos]->ce_flags |= flag;
+                       the_index.cache[pos]->ce_flags |= flag;
                else
-                       active_cache[pos]->ce_flags &= ~flag;
-               active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+                       the_index.cache[pos]->ce_flags &= ~flag;
+               the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
                cache_tree_invalidate_path(&the_index, path);
-               active_cache_changed |= CE_ENTRY_CHANGED;
+               the_index.cache_changed |= CE_ENTRY_CHANGED;
                return 0;
        }
        return -1;
@@ -249,7 +266,7 @@ static int remove_one_path(const char *path)
 {
        if (!allow_remove)
                return error("%s: does not exist and --remove not passed", path);
-       if (remove_file_from_cache(path))
+       if (remove_file_from_index(&the_index, path))
                return error("%s: cannot remove from the index", path);
        return 0;
 }
@@ -274,7 +291,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        struct cache_entry *ce;
 
        /* Was the old index entry already up-to-date? */
-       if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
+       if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
                return 0;
 
        ce = make_empty_cache_entry(&the_index, len);
@@ -291,7 +308,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
        }
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option)) {
+       if (add_index_entry(&the_index, ce, option)) {
                discard_cache_entry(ce);
                return error("%s: cannot add to the index - missing --add option?", path);
        }
@@ -324,11 +341,11 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 static int process_directory(const char *path, int len, struct stat *st)
 {
        struct object_id oid;
-       int pos = cache_name_pos(path, len);
+       int pos = index_name_pos(&the_index, path, len);
 
        /* Exact match: file or existing gitlink */
        if (pos >= 0) {
-               const struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = the_index.cache[pos];
                if (S_ISGITLINK(ce->ce_mode)) {
 
                        /* Do nothing to the index if there is no HEAD! */
@@ -343,8 +360,8 @@ static int process_directory(const char *path, int len, struct stat *st)
 
        /* Inexact match: is there perhaps a subdirectory match? */
        pos = -pos-1;
-       while (pos < active_nr) {
-               const struct cache_entry *ce = active_cache[pos++];
+       while (pos < the_index.cache_nr) {
+               const struct cache_entry *ce = the_index.cache[pos++];
 
                if (strncmp(ce->name, path, len))
                        break;
@@ -374,8 +391,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
        if (has_symlink_leading_path(path, len))
                return error("'%s' is beyond a symbolic link", path);
 
-       pos = cache_name_pos(path, len);
-       ce = pos < 0 ? NULL : active_cache[pos];
+       pos = index_name_pos(&the_index, path, len);
+       ce = pos < 0 ? NULL : the_index.cache[pos];
        if (ce && ce_skip_worktree(ce)) {
                /*
                 * working directory version is assumed "good"
@@ -383,7 +400,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
                 * On the other hand, removing it from index should work
                 */
                if (!ignore_skip_worktree_entries && allow_remove &&
-                   remove_file_from_cache(path))
+                   remove_file_from_index(&the_index, path))
                        return error("%s: cannot remove from the index", path);
                return 0;
        }
@@ -422,7 +439,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
                ce->ce_flags |= CE_VALID;
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-       if (add_cache_entry(ce, option))
+       if (add_index_entry(&the_index, ce, option))
                return error("%s: cannot add to the index - missing --add option?",
                             path);
        report("add '%s'", path);
@@ -434,11 +451,11 @@ static void chmod_path(char flip, const char *path)
        int pos;
        struct cache_entry *ce;
 
-       pos = cache_name_pos(path, strlen(path));
+       pos = index_name_pos(&the_index, path, strlen(path));
        if (pos < 0)
                goto fail;
-       ce = active_cache[pos];
-       if (chmod_cache_entry(ce, flip) < 0)
+       ce = the_index.cache[pos];
+       if (chmod_index_entry(&the_index, ce, flip) < 0)
                goto fail;
 
        report("chmod %cx '%s'", flip, path);
@@ -481,7 +498,7 @@ static void update_one(const char *path)
        }
 
        if (force_remove) {
-               if (remove_file_from_cache(path))
+               if (remove_file_from_index(&the_index, path))
                        die("git update-index: unable to remove %s", path);
                report("remove '%s'", path);
                return;
@@ -564,7 +581,7 @@ static void read_index_info(int nul_term_line)
 
                if (!mode) {
                        /* mode == 0 means there is no such path -- remove */
-                       if (remove_file_from_cache(path_name))
+                       if (remove_file_from_index(&the_index, path_name))
                                die("git update-index: unable to remove %s",
                                    ptr);
                }
@@ -608,7 +625,7 @@ static struct cache_entry *read_one_ent(const char *which,
                        error("%s: not in %s branch.", path, which);
                return NULL;
        }
-       if (mode == S_IFDIR) {
+       if (!the_index.sparse_index && mode == S_IFDIR) {
                if (which)
                        error("%s: not a blob in %s branch.", path, which);
                return NULL;
@@ -631,12 +648,12 @@ static int unresolve_one(const char *path)
        struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
 
        /* See if there is such entry in the index. */
-       pos = cache_name_pos(path, namelen);
+       pos = index_name_pos(&the_index, path, namelen);
        if (0 <= pos) {
                /* already merged */
-               pos = unmerge_cache_entry_at(pos);
-               if (pos < active_nr) {
-                       const struct cache_entry *ce = active_cache[pos];
+               pos = unmerge_index_entry_at(&the_index, pos);
+               if (pos < the_index.cache_nr) {
+                       const struct cache_entry *ce = the_index.cache[pos];
                        if (ce_stage(ce) &&
                            ce_namelen(ce) == namelen &&
                            !memcmp(ce->name, path, namelen))
@@ -649,8 +666,8 @@ static int unresolve_one(const char *path)
                 * want to do anything in the former case.
                 */
                pos = -pos-1;
-               if (pos < active_nr) {
-                       const struct cache_entry *ce = active_cache[pos];
+               if (pos < the_index.cache_nr) {
+                       const struct cache_entry *ce = the_index.cache[pos];
                        if (ce_namelen(ce) == namelen &&
                            !memcmp(ce->name, path, namelen)) {
                                fprintf(stderr,
@@ -679,13 +696,13 @@ static int unresolve_one(const char *path)
                goto free_return;
        }
 
-       remove_file_from_cache(path);
-       if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
+       remove_file_from_index(&the_index, path);
+       if (add_index_entry(&the_index, ce_2, ADD_CACHE_OK_TO_ADD)) {
                error("%s: cannot add our version to the index.", path);
                ret = -1;
                goto free_return;
        }
-       if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
+       if (!add_index_entry(&the_index, ce_3, ADD_CACHE_OK_TO_ADD))
                return 0;
        error("%s: cannot add their version to the index.", path);
        ret = -1;
@@ -725,7 +742,7 @@ static int do_unresolve(int ac, const char **av,
        return err;
 }
 
-static int do_reupdate(int ac, const char **av,
+static int do_reupdate(const char **paths,
                       const char *prefix)
 {
        /* Read HEAD and run update-index on paths that are
@@ -737,7 +754,7 @@ static int do_reupdate(int ac, const char **av,
 
        parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_CWD,
-                      prefix, av + 1);
+                      prefix, paths);
 
        if (read_ref("HEAD", &head_oid))
                /* If there is no HEAD, that means it is an initial
@@ -745,10 +762,8 @@ static int do_reupdate(int ac, const char **av,
                 */
                has_head = 0;
  redo:
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(&the_index);
-       for (pos = 0; pos < active_nr; pos++) {
-               const struct cache_entry *ce = active_cache[pos];
+       for (pos = 0; pos < the_index.cache_nr; pos++) {
+               const struct cache_entry *ce = the_index.cache[pos];
                struct cache_entry *old = NULL;
                int save_nr;
                char *path;
@@ -763,16 +778,26 @@ static int do_reupdate(int ac, const char **av,
                        discard_cache_entry(old);
                        continue; /* unchanged */
                }
+
+               /* At this point, we know the contents of the sparse directory are
+                * modified with respect to HEAD, so we expand the index and restart
+                * to process each path individually
+                */
+               if (S_ISSPARSEDIR(ce->ce_mode)) {
+                       ensure_full_index(&the_index);
+                       goto redo;
+               }
+
                /* Be careful.  The working tree may not have the
                 * path anymore, in which case, under 'allow_remove',
                 * or worse yet 'allow_replace', active_nr may decrease.
                 */
-               save_nr = active_nr;
+               save_nr = the_index.cache_nr;
                path = xstrdup(ce->name);
                update_one(path);
                free(path);
                discard_cache_entry(old);
-               if (save_nr != active_nr)
+               if (save_nr != the_index.cache_nr)
                        goto redo;
        }
        clear_pathspec(&pathspec);
@@ -787,8 +812,20 @@ struct refresh_params {
 static int refresh(struct refresh_params *o, unsigned int flag)
 {
        setup_work_tree();
-       read_cache();
-       *o->has_errors |= refresh_cache(o->flags | flag);
+       repo_read_index(the_repository);
+       *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+                                       NULL, NULL);
+       if (has_racy_timestamp(&the_index)) {
+               /*
+                * Even if nothing else has changed, updating the file
+                * increases the chance that racy timestamps become
+                * non-racy, helping future run-time performance.
+                * We do that even in case of "errors" returned by
+                * refresh_index() as these are no actual errors.
+                * cmd_status() does the same.
+                */
+               the_index.cache_changed |= SOMETHING_CHANGED;
+       }
        return 0;
 }
 
@@ -824,7 +861,7 @@ static int resolve_undo_clear_callback(const struct option *opt,
 {
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
-       resolve_undo_clear();
+       resolve_undo_clear_index(&the_index);
        return 0;
 }
 
@@ -925,7 +962,7 @@ static enum parse_opt_result unresolve_callback(
        *has_errors = do_unresolve(ctx->argc, ctx->argv,
                                prefix, prefix ? strlen(prefix) : 0);
        if (*has_errors)
-               active_cache_changed = 0;
+               the_index.cache_changed = 0;
 
        ctx->argv += ctx->argc - 1;
        ctx->argc = 1;
@@ -944,9 +981,9 @@ static enum parse_opt_result reupdate_callback(
 
        /* consume remaining arguments. */
        setup_work_tree();
-       *has_errors = do_reupdate(ctx->argc, ctx->argv, prefix);
+       *has_errors = do_reupdate(ctx->argv + 1, prefix);
        if (*has_errors)
-               active_cache_changed = 0;
+               the_index.cache_changed = 0;
 
        ctx->argv += ctx->argc - 1;
        ctx->argc = 1;
@@ -1079,12 +1116,15 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
+       prepare_repo_settings(r);
+       the_repository->settings.command_requires_full_index = 0;
+
        /* we will diagnose later if it turns out that we need to update it */
-       newfd = hold_locked_index(&lock_file, 0);
+       newfd = repo_hold_locked_index(the_repository, &lock_file, 0);
        if (newfd < 0)
                lock_error = errno;
 
-       entries = read_cache();
+       entries = repo_read_index(the_repository);
        if (entries < 0)
                die("cache corrupted");
 
@@ -1096,6 +1136,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
         */
        parse_options_start(&ctx, argc, argv, prefix,
                            options, PARSE_OPT_STOP_AT_NON_OPTION);
+
+       /*
+        * Allow the object layer to optimize adding multiple objects in
+        * a batch.
+        */
+       begin_odb_transaction();
        while (ctx.argc) {
                if (parseopt_state != PARSE_OPT_DONE)
                        parseopt_state = parse_options_step(&ctx, options,
@@ -1143,7 +1189,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                            INDEX_FORMAT_LB, INDEX_FORMAT_UB);
 
                if (the_index.version != preferred_index_format)
-                       active_cache_changed |= SOMETHING_CHANGED;
+                       the_index.cache_changed |= SOMETHING_CHANGED;
                the_index.version = preferred_index_format;
        }
 
@@ -1170,6 +1216,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        }
 
+       /*
+        * By now we have added all of the new objects
+        */
+       end_odb_transaction();
+
        if (split_index > 0) {
                if (git_config_get_split_index() == 0)
                        warning(_("core.splitIndex is set to false; "
@@ -1216,14 +1267,33 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        }
 
        if (fsmonitor > 0) {
-               if (git_config_get_fsmonitor() == 0)
+               enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
+               enum fsmonitor_reason reason = fsm_settings__get_reason(r);
+
+               /*
+                * The user wants to turn on FSMonitor using the command
+                * line argument.  (We don't know (or care) whether that
+                * is the IPC or HOOK version.)
+                *
+                * Use one of the __get routines to force load the FSMonitor
+                * config settings into the repo-settings.  That will detect
+                * whether the file system is compatible so that we can stop
+                * here with a nice error message.
+                */
+               if (reason > FSMONITOR_REASON_OK)
+                       die("%s",
+                           fsm_settings__get_incompatible_msg(r, reason));
+
+               if (fsm_mode == FSMONITOR_MODE_DISABLED) {
                        warning(_("core.fsmonitor is unset; "
                                "set it if you really want to "
                                "enable fsmonitor"));
+               }
                add_fsmonitor(&the_index);
                report(_("fsmonitor enabled"));
        } else if (!fsmonitor) {
-               if (git_config_get_fsmonitor() == 1)
+               enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
+               if (fsm_mode > FSMONITOR_MODE_DISABLED)
                        warning(_("core.fsmonitor is set; "
                                "remove it if you really want to "
                                "disable fsmonitor"));
@@ -1231,7 +1301,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                report(_("fsmonitor disabled"));
        }
 
-       if (active_cache_changed || force_write) {
+       if (the_index.cache_changed || force_write) {
                if (newfd < 0) {
                        if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);