]> git.ipfire.org Git - thirdparty/git.git/blobdiff - entry.c
unpack-trees: add basic support for parallel checkout
[thirdparty/git.git] / entry.c
diff --git a/entry.c b/entry.c
index 7b9f43716f76e6e7a701966045429b263cfb691e..d7ed38aa407990b3a14f1a3598cecc4b5e8ddd8b 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -6,6 +6,8 @@
 #include "submodule.h"
 #include "progress.h"
 #include "fsmonitor.h"
+#include "entry.h"
+#include "parallel-checkout.h"
 
 static void create_directories(const char *path, int path_len,
                               const struct checkout *state)
@@ -83,7 +85,7 @@ static int create_file(const char *path, unsigned int mode)
        return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
 }
 
-static void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
+void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
 {
        enum object_type type;
        void *blob_data = read_object_file(&ce->oid, &type, size);
@@ -108,7 +110,7 @@ static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempf
        }
 }
 
-static int fstat_output(int fd, const struct checkout *state, struct stat *st)
+int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st)
 {
        /* use fstat() only when path == ce->name */
        if (fstat_is_reliable() &&
@@ -131,7 +133,7 @@ static int streaming_write_entry(const struct cache_entry *ce, char *path,
                return -1;
 
        result |= stream_blob_to_fd(fd, &ce->oid, filter, 1);
-       *fstat_done = fstat_output(fd, state, statbuf);
+       *fstat_done = fstat_checkout_output(fd, state, statbuf);
        result |= close(fd);
 
        if (result)
@@ -250,8 +252,21 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts)
        return errs;
 }
 
-static int write_entry(struct cache_entry *ce,
-                      char *path, const struct checkout *state, int to_tempfile)
+void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
+                          struct stat *st)
+{
+       if (state->refresh_cache) {
+               assert(state->istate);
+               fill_stat_cache_info(state->istate, ce, st);
+               ce->ce_flags |= CE_UPDATE_IN_BASE;
+               mark_fsmonitor_invalid(state->istate, ce);
+               state->istate->cache_changed |= CE_ENTRY_CHANGED;
+       }
+}
+
+/* Note: ca is used (and required) iff the entry refers to a regular file. */
+static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca,
+                      const struct checkout *state, int to_tempfile)
 {
        unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
        struct delayed_checkout *dco = state->delayed_checkout;
@@ -268,8 +283,7 @@ static int write_entry(struct cache_entry *ce,
        clone_checkout_metadata(&meta, &state->meta, &ce->oid);
 
        if (ce_mode_s_ifmt == S_IFREG) {
-               struct stream_filter *filter = get_stream_filter(state->istate, ce->name,
-                                                                &ce->oid);
+               struct stream_filter *filter = get_stream_filter_ca(ca, &ce->oid);
                if (filter &&
                    !streaming_write_entry(ce, path, filter,
                                           state, to_tempfile,
@@ -316,14 +330,17 @@ static int write_entry(struct cache_entry *ce,
                 * Convert from git internal format to working tree format
                 */
                if (dco && dco->state != CE_NO_DELAY) {
-                       ret = async_convert_to_working_tree(state->istate, ce->name, new_blob,
-                                                           size, &buf, &meta, dco);
+                       ret = async_convert_to_working_tree_ca(ca, ce->name,
+                                                              new_blob, size,
+                                                              &buf, &meta, dco);
                        if (ret && string_list_has_string(&dco->paths, ce->name)) {
                                free(new_blob);
                                goto delayed;
                        }
-               } else
-                       ret = convert_to_working_tree(state->istate, ce->name, new_blob, size, &buf, &meta);
+               } else {
+                       ret = convert_to_working_tree_ca(ca, ce->name, new_blob,
+                                                        size, &buf, &meta);
+               }
 
                if (ret) {
                        free(new_blob);
@@ -345,7 +362,7 @@ static int write_entry(struct cache_entry *ce,
 
                wrote = write_in_full(fd, new_blob, size);
                if (!to_tempfile)
-                       fstat_done = fstat_output(fd, state, &st);
+                       fstat_done = fstat_checkout_output(fd, state, &st);
                close(fd);
                free(new_blob);
                if (wrote < 0)
@@ -370,15 +387,10 @@ static int write_entry(struct cache_entry *ce,
 
 finish:
        if (state->refresh_cache) {
-               assert(state->istate);
-               if (!fstat_done)
-                       if (lstat(ce->name, &st) < 0)
-                               return error_errno("unable to stat just-written file %s",
-                                                  ce->name);
-               fill_stat_cache_info(state->istate, ce, &st);
-               ce->ce_flags |= CE_UPDATE_IN_BASE;
-               mark_fsmonitor_invalid(state->istate, ce);
-               state->istate->cache_changed |= CE_ENTRY_CHANGED;
+               if (!fstat_done && lstat(ce->name, &st) < 0)
+                       return error_errno("unable to stat just-written file %s",
+                                          ce->name);
+               update_ce_after_write(state, ce , &st);
        }
 delayed:
        return 0;
@@ -415,8 +427,17 @@ static void mark_colliding_entries(const struct checkout *state,
        for (i = 0; i < state->istate->cache_nr; i++) {
                struct cache_entry *dup = state->istate->cache[i];
 
-               if (dup == ce)
-                       break;
+               if (dup == ce) {
+                       /*
+                        * Parallel checkout doesn't create the files in index
+                        * order. So the other side of the collision may appear
+                        * after the given cache_entry in the array.
+                        */
+                       if (parallel_checkout_status() == PC_RUNNING)
+                               continue;
+                       else
+                               break;
+               }
 
                if (dup->ce_flags & (CE_MATCHED | CE_VALID | CE_SKIP_WORKTREE))
                        continue;
@@ -429,19 +450,13 @@ static void mark_colliding_entries(const struct checkout *state,
        }
 }
 
-/*
- * Write the contents from ce out to the working tree.
- *
- * When topath[] is not NULL, instead of writing to the working tree
- * file named by ce, a temporary file is created by this function and
- * its name is returned in topath[], which must be able to hold at
- * least TEMPORARY_FILENAME_LENGTH bytes long.
- */
-int checkout_entry(struct cache_entry *ce, const struct checkout *state,
-                  char *topath, int *nr_checkouts)
+int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
+                     const struct checkout *state, char *topath,
+                     int *nr_checkouts)
 {
        static struct strbuf path = STRBUF_INIT;
        struct stat st;
+       struct conv_attrs ca_buf;
 
        if (ce->ce_flags & CE_WT_REMOVE) {
                if (topath)
@@ -454,8 +469,13 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state,
                return 0;
        }
 
-       if (topath)
-               return write_entry(ce, topath, state, 1);
+       if (topath) {
+               if (S_ISREG(ce->ce_mode) && !ca) {
+                       convert_attrs(state->istate, &ca_buf, ce->name);
+                       ca = &ca_buf;
+               }
+               return write_entry(ce, topath, ca, state, 1);
+       }
 
        strbuf_reset(&path);
        strbuf_add(&path, state->base_dir, state->base_dir_len);
@@ -517,9 +537,19 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state,
                return 0;
 
        create_directories(path.buf, path.len, state);
+
        if (nr_checkouts)
                (*nr_checkouts)++;
-       return write_entry(ce, path.buf, state, 0);
+
+       if (S_ISREG(ce->ce_mode) && !ca) {
+               convert_attrs(state->istate, &ca_buf, ce->name);
+               ca = &ca_buf;
+       }
+
+       if (!enqueue_checkout(ce, ca))
+               return 0;
+
+       return write_entry(ce, path.buf, ca, state, 0);
 }
 
 void unlink_entry(const struct cache_entry *ce)
@@ -530,7 +560,7 @@ void unlink_entry(const struct cache_entry *ce)
                submodule_move_head(ce->name, "HEAD", NULL,
                                    SUBMODULE_MOVE_HEAD_FORCE);
        }
-       if (!check_leading_path(ce->name, ce_namelen(ce)))
+       if (check_leading_path(ce->name, ce_namelen(ce), 1) >= 0)
                return;
        if (remove_or_warn(ce->ce_mode, ce->name))
                return;