Merge branch 'jk/fast-import-history-bugfix'
[gitweb.git] / builtin / clone.c
index 3623f040d4086458b8e36e5e701d892c1d426078..2048b6760aa9f0a426524e5ea1bd7a3a4c4c5536 100644 (file)
@@ -23,6 +23,8 @@
 #include "transport.h"
 #include "strbuf.h"
 #include "dir.h"
+#include "dir-iterator.h"
+#include "iterator.h"
 #include "sigchain.h"
 #include "branch.h"
 #include "remote.h"
@@ -67,6 +69,7 @@ static int max_jobs = -1;
 static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
 static struct list_objects_filter_options filter_options;
 static struct string_list server_options = STRING_LIST_INIT_NODUP;
+static int option_remote_submodules;
 
 static int recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@ -142,6 +145,8 @@ static struct option builtin_clone_options[] = {
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+       OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
+                   N_("any cloned submodules will use their remote-tracking branch")),
        OPT_END()
 };
 
@@ -354,8 +359,7 @@ static void setup_reference(void)
                             add_one_reference, &required);
 }
 
-static void copy_alternates(struct strbuf *src, struct strbuf *dst,
-                           const char *src_repo)
+static void copy_alternates(struct strbuf *src, const char *src_repo)
 {
        /*
         * Read from the source objects/info/alternates file
@@ -392,58 +396,63 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
        fclose(in);
 }
 
+static void mkdir_if_missing(const char *pathname, mode_t mode)
+{
+       struct stat st;
+
+       if (!mkdir(pathname, mode))
+               return;
+
+       if (errno != EEXIST)
+               die_errno(_("failed to create directory '%s'"), pathname);
+       else if (stat(pathname, &st))
+               die_errno(_("failed to stat '%s'"), pathname);
+       else if (!S_ISDIR(st.st_mode))
+               die(_("%s exists and is not a directory"), pathname);
+}
+
 static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
-                                  const char *src_repo, int src_baselen)
+                                  const char *src_repo)
 {
-       struct dirent *de;
-       struct stat buf;
        int src_len, dest_len;
-       DIR *dir;
-
-       dir = opendir(src->buf);
-       if (!dir)
-               die_errno(_("failed to open '%s'"), src->buf);
-
-       if (mkdir(dest->buf, 0777)) {
-               if (errno != EEXIST)
-                       die_errno(_("failed to create directory '%s'"), dest->buf);
-               else if (stat(dest->buf, &buf))
-                       die_errno(_("failed to stat '%s'"), dest->buf);
-               else if (!S_ISDIR(buf.st_mode))
-                       die(_("%s exists and is not a directory"), dest->buf);
-       }
+       struct dir_iterator *iter;
+       int iter_status;
+       unsigned int flags;
+
+       mkdir_if_missing(dest->buf, 0777);
+
+       flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
+       iter = dir_iterator_begin(src->buf, flags);
+
+       if (!iter)
+               die_errno(_("failed to start iterator over '%s'"), src->buf);
 
        strbuf_addch(src, '/');
        src_len = src->len;
        strbuf_addch(dest, '/');
        dest_len = dest->len;
 
-       while ((de = readdir(dir)) != NULL) {
+       while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
                strbuf_setlen(src, src_len);
-               strbuf_addstr(src, de->d_name);
+               strbuf_addstr(src, iter->relative_path);
                strbuf_setlen(dest, dest_len);
-               strbuf_addstr(dest, de->d_name);
-               if (stat(src->buf, &buf)) {
-                       warning (_("failed to stat %s\n"), src->buf);
-                       continue;
-               }
-               if (S_ISDIR(buf.st_mode)) {
-                       if (de->d_name[0] != '.')
-                               copy_or_link_directory(src, dest,
-                                                      src_repo, src_baselen);
+               strbuf_addstr(dest, iter->relative_path);
+
+               if (S_ISDIR(iter->st.st_mode)) {
+                       mkdir_if_missing(dest->buf, 0777);
                        continue;
                }
 
                /* Files that cannot be copied bit-for-bit... */
-               if (!strcmp(src->buf + src_baselen, "/info/alternates")) {
-                       copy_alternates(src, dest, src_repo);
+               if (!fspathcmp(iter->relative_path, "info/alternates")) {
+                       copy_alternates(src, src_repo);
                        continue;
                }
 
                if (unlink(dest->buf) && errno != ENOENT)
                        die_errno(_("failed to unlink '%s'"), dest->buf);
                if (!option_no_hardlinks) {
-                       if (!link(src->buf, dest->buf))
+                       if (!link(real_path(src->buf), dest->buf))
                                continue;
                        if (option_local > 0)
                                die_errno(_("failed to create link '%s'"), dest->buf);
@@ -452,7 +461,11 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                if (copy_file_with_time(dest->buf, src->buf, 0666))
                        die_errno(_("failed to copy file to '%s'"), dest->buf);
        }
-       closedir(dir);
+
+       if (iter_status != ITER_DONE) {
+               strbuf_setlen(src, src_len);
+               die(_("failed to iterate over '%s'"), src->buf);
+       }
 }
 
 static void clone_local(const char *src_repo, const char *dest_repo)
@@ -470,7 +483,7 @@ static void clone_local(const char *src_repo, const char *dest_repo)
                get_common_dir(&dest, dest_repo);
                strbuf_addstr(&src, "/objects");
                strbuf_addstr(&dest, "/objects");
-               copy_or_link_directory(&src, &dest, src_repo, src.len);
+               copy_or_link_directory(&src, &dest, src_repo);
                strbuf_release(&src);
                strbuf_release(&dest);
        }
@@ -492,7 +505,7 @@ static enum {
 static const char junk_leave_repo_msg[] =
 N_("Clone succeeded, but checkout failed.\n"
    "You can inspect what was checked out with 'git status'\n"
-   "and retry the checkout with 'git checkout -f HEAD'\n");
+   "and retry with 'git restore --source=HEAD :/'\n");
 
 static void remove_junk(void)
 {
@@ -791,6 +804,11 @@ static int checkout(int submodule_progress)
                if (option_verbosity < 0)
                        argv_array_push(&args, "--quiet");
 
+               if (option_remote_submodules) {
+                       argv_array_push(&args, "--remote");
+                       argv_array_push(&args, "--no-fetch");
+               }
+
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@ -1142,13 +1160,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport->server_options = &server_options;
 
        if (filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&filter_options,
-                                               &expanded_filter_spec);
+               const char *spec =
+                       expand_list_objects_filter_spec(&filter_options);
                transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                                    expanded_filter_spec.buf);
+                                    spec);
                transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
-               strbuf_release(&expanded_filter_spec);
        }
 
        if (transport->smart_options && !deepen && !filter_options.choice)