p5551: add a script to test fetch pack-dir rescans
[gitweb.git] / builtin / pull.c
index 398aae16c006f5386ba6d65e3ef266b4ddfe1e74..9b86e519b19a6180e52fda3342dec4f080e1f9e9 100644 (file)
@@ -6,6 +6,7 @@
  * Fetch one or more remote refs and merge it/them into the current HEAD.
  */
 #include "cache.h"
+#include "config.h"
 #include "builtin.h"
 #include "parse-options.h"
 #include "exec_cmd.h"
 #include "dir.h"
 #include "refs.h"
 #include "revision.h"
+#include "submodule.h"
+#include "submodule-config.h"
 #include "tempfile.h"
 #include "lockfile.h"
+#include "wt-status.h"
 
 enum rebase_type {
        REBASE_INVALID = -1,
@@ -76,6 +80,7 @@ static const char * const pull_usage[] = {
 /* Shared options */
 static int opt_verbosity;
 static char *opt_progress;
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
@@ -100,7 +105,6 @@ static char *opt_upload_pack;
 static int opt_force;
 static char *opt_tags;
 static char *opt_prune;
-static char *opt_recurse_submodules;
 static char *max_children;
 static int opt_dry_run;
 static char *opt_keep;
@@ -115,6 +119,10 @@ static struct option pull_options[] = {
        OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
                N_("force progress reporting"),
                PARSE_OPT_NOARG),
+       { OPTION_CALLBACK, 0, "recurse-submodules",
+                  &recurse_submodules, N_("on-demand"),
+                  N_("control for recursive fetching of submodules"),
+                  PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
 
        /* Options passed to git-merge or git-rebase */
        OPT_GROUP(N_("Options related to merging")),
@@ -186,10 +194,6 @@ static struct option pull_options[] = {
        OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
                N_("prune remote-tracking branches no longer on remote"),
                PARSE_OPT_NOARG),
-       OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules,
-               N_("on-demand"),
-               N_("control recursive fetching of submodules"),
-               PARSE_OPT_OPTARG),
        OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
                N_("number of submodules pulled in parallel"),
                PARSE_OPT_OPTARG),
@@ -325,92 +329,24 @@ static int git_pull_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-/**
- * Returns 1 if there are unstaged changes, 0 otherwise.
- */
-static int has_unstaged_changes(const char *prefix)
-{
-       struct rev_info rev_info;
-       int result;
-
-       init_revisions(&rev_info, prefix);
-       DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
-       diff_setup_done(&rev_info.diffopt);
-       result = run_diff_files(&rev_info, 0);
-       return diff_result_code(&rev_info.diffopt, result);
-}
-
-/**
- * Returns 1 if there are uncommitted changes, 0 otherwise.
- */
-static int has_uncommitted_changes(const char *prefix)
-{
-       struct rev_info rev_info;
-       int result;
-
-       if (is_cache_unborn())
-               return 0;
-
-       init_revisions(&rev_info, prefix);
-       DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
-       add_head_to_pending(&rev_info);
-       diff_setup_done(&rev_info.diffopt);
-       result = run_diff_index(&rev_info, 1);
-       return diff_result_code(&rev_info.diffopt, result);
-}
-
-/**
- * If the work tree has unstaged or uncommitted changes, dies with the
- * appropriate message.
- */
-static void die_on_unclean_work_tree(const char *prefix)
-{
-       struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
-       int do_die = 0;
-
-       hold_locked_index(lock_file, 0);
-       refresh_cache(REFRESH_QUIET);
-       update_index_if_able(&the_index, lock_file);
-       rollback_lock_file(lock_file);
-
-       if (has_unstaged_changes(prefix)) {
-               error(_("Cannot pull with rebase: You have unstaged changes."));
-               do_die = 1;
-       }
-
-       if (has_uncommitted_changes(prefix)) {
-               if (do_die)
-                       error(_("Additionally, your index contains uncommitted changes."));
-               else
-                       error(_("Cannot pull with rebase: Your index contains uncommitted changes."));
-               do_die = 1;
-       }
-
-       if (do_die)
-               exit(1);
-}
-
 /**
  * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge
  * into merge_heads.
  */
-static void get_merge_heads(struct sha1_array *merge_heads)
+static void get_merge_heads(struct oid_array *merge_heads)
 {
-       const char *filename = git_path("FETCH_HEAD");
+       const char *filename = git_path_fetch_head();
        FILE *fp;
        struct strbuf sb = STRBUF_INIT;
-       unsigned char sha1[GIT_SHA1_RAWSZ];
+       struct object_id oid;
 
-       if (!(fp = fopen(filename, "r")))
-               die_errno(_("could not open '%s' for reading"), filename);
+       fp = xfopen(filename, "r");
        while (strbuf_getline_lf(&sb, fp) != EOF) {
-               if (get_sha1_hex(sb.buf, sha1))
+               if (get_oid_hex(sb.buf, &oid))
                        continue;  /* invalid line: does not start with SHA1 */
                if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t"))
                        continue;  /* ref is not-for-merge */
-               sha1_array_append(merge_heads, sha1);
+               oid_array_append(merge_heads, &oid);
        }
        fclose(fp);
        strbuf_release(&sb);
@@ -550,8 +486,20 @@ static int run_fetch(const char *repo, const char **refspecs)
                argv_array_push(&args, opt_tags);
        if (opt_prune)
                argv_array_push(&args, opt_prune);
-       if (opt_recurse_submodules)
-               argv_array_push(&args, opt_recurse_submodules);
+       if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+               switch (recurse_submodules) {
+               case RECURSE_SUBMODULES_ON:
+                       argv_array_push(&args, "--recurse-submodules=on");
+                       break;
+               case RECURSE_SUBMODULES_OFF:
+                       argv_array_push(&args, "--recurse-submodules=no");
+                       break;
+               case RECURSE_SUBMODULES_ON_DEMAND:
+                       argv_array_push(&args, "--recurse-submodules=on-demand");
+                       break;
+               default:
+                       BUG("submodule recursion option not understood");
+               }
        if (max_children)
                argv_array_push(&args, max_children);
        if (opt_dry_run)
@@ -580,8 +528,8 @@ static int run_fetch(const char *repo, const char **refspecs)
 /**
  * "Pulls into void" by branching off merge_head.
  */
-static int pull_into_void(const unsigned char *merge_head,
-               const unsigned char *curr_head)
+static int pull_into_void(const struct object_id *merge_head,
+               const struct object_id *curr_head)
 {
        /*
         * Two-way merge: we treat the index as based on an empty tree,
@@ -589,15 +537,39 @@ static int pull_into_void(const unsigned char *merge_head,
         * index/worktree changes that the user already made on the unborn
         * branch.
         */
-       if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0))
+       if (checkout_fast_forward(&empty_tree_oid, merge_head, 0))
                return 1;
 
-       if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+       if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR))
                return 1;
 
        return 0;
 }
 
+static int rebase_submodules(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       argv_array_pushl(&cp.args, "submodule", "update",
+                                  "--recursive", "--rebase", NULL);
+
+       return run_command(&cp);
+}
+
+static int update_submodules(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       argv_array_pushl(&cp.args, "submodule", "update",
+                                  "--recursive", "--checkout", NULL);
+
+       return run_command(&cp);
+}
+
 /**
  * Runs git-merge, returning its exit status.
  */
@@ -713,7 +685,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)
  * current branch forked from its remote tracking branch. Returns 0 on success,
  * -1 on failure.
  */
-static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
+static int get_rebase_fork_point(struct object_id *fork_point, const char *repo,
                const char *refspec)
 {
        int ret;
@@ -744,7 +716,7 @@ static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
        if (ret)
                goto cleanup;
 
-       ret = get_sha1_hex(sb.buf, fork_point);
+       ret = get_oid_hex(sb.buf, fork_point);
        if (ret)
                goto cleanup;
 
@@ -757,16 +729,16 @@ static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
  * Sets merge_base to the octopus merge base of curr_head, merge_head and
  * fork_point. Returns 0 if a merge base is found, 1 otherwise.
  */
-static int get_octopus_merge_base(unsigned char *merge_base,
-               const unsigned char *curr_head,
-               const unsigned char *merge_head,
-               const unsigned char *fork_point)
+static int get_octopus_merge_base(struct object_id *merge_base,
+               const struct object_id *curr_head,
+               const struct object_id *merge_head,
+               const struct object_id *fork_point)
 {
        struct commit_list *revs = NULL, *result;
 
        commit_list_insert(lookup_commit_reference(curr_head), &revs);
        commit_list_insert(lookup_commit_reference(merge_head), &revs);
-       if (!is_null_sha1(fork_point))
+       if (!is_null_oid(fork_point))
                commit_list_insert(lookup_commit_reference(fork_point), &revs);
 
        result = reduce_heads(get_octopus_merge_bases(revs));
@@ -774,7 +746,7 @@ static int get_octopus_merge_base(unsigned char *merge_base,
        if (!result)
                return 1;
 
-       hashcpy(merge_base, result->item->object.oid.hash);
+       oidcpy(merge_base, &result->item->object.oid);
        return 0;
 }
 
@@ -783,16 +755,16 @@ static int get_octopus_merge_base(unsigned char *merge_base,
  * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
  * appropriate arguments and returns its exit status.
  */
-static int run_rebase(const unsigned char *curr_head,
-               const unsigned char *merge_head,
-               const unsigned char *fork_point)
+static int run_rebase(const struct object_id *curr_head,
+               const struct object_id *merge_head,
+               const struct object_id *fork_point)
 {
        int ret;
-       unsigned char oct_merge_base[GIT_SHA1_RAWSZ];
+       struct object_id oct_merge_base;
        struct argv_array args = ARGV_ARRAY_INIT;
 
-       if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point))
-               if (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point))
+       if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point))
+               if (!is_null_oid(fork_point) && !oidcmp(&oct_merge_base, fork_point))
                        fork_point = NULL;
 
        argv_array_push(&args, "rebase");
@@ -820,12 +792,12 @@ static int run_rebase(const unsigned char *curr_head,
                warning(_("ignoring --verify-signatures for rebase"));
 
        argv_array_push(&args, "--onto");
-       argv_array_push(&args, sha1_to_hex(merge_head));
+       argv_array_push(&args, oid_to_hex(merge_head));
 
-       if (fork_point && !is_null_sha1(fork_point))
-               argv_array_push(&args, sha1_to_hex(fork_point));
+       if (fork_point && !is_null_oid(fork_point))
+               argv_array_push(&args, oid_to_hex(fork_point));
        else
-               argv_array_push(&args, sha1_to_hex(merge_head));
+               argv_array_push(&args, oid_to_hex(merge_head));
 
        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
        argv_array_clear(&args);
@@ -835,9 +807,10 @@ static int run_rebase(const unsigned char *curr_head,
 int cmd_pull(int argc, const char **argv, const char *prefix)
 {
        const char *repo, **refspecs;
-       struct sha1_array merge_heads = SHA1_ARRAY_INIT;
-       unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];
-       unsigned char rebase_fork_point[GIT_SHA1_RAWSZ];
+       struct oid_array merge_heads = OID_ARRAY_INIT;
+       struct object_id orig_head, curr_head;
+       struct object_id rebase_fork_point;
+       int autostash;
 
        if (!getenv("GIT_REFLOG_ACTION"))
                set_reflog_message(argc, argv);
@@ -857,28 +830,29 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (read_cache_unmerged())
                die_resolve_conflict("pull");
 
-       if (file_exists(git_path("MERGE_HEAD")))
+       if (file_exists(git_path_merge_head()))
                die_conclude_merge();
 
-       if (get_sha1("HEAD", orig_head))
-               hashclr(orig_head);
+       if (get_oid("HEAD", &orig_head))
+               oidclr(&orig_head);
 
        if (!opt_rebase && opt_autostash != -1)
                die(_("--[no-]autostash option is only valid with --rebase."));
 
+       autostash = config_autostash;
        if (opt_rebase) {
-               int autostash = config_autostash;
                if (opt_autostash != -1)
                        autostash = opt_autostash;
 
-               if (is_null_sha1(orig_head) && !is_cache_unborn())
+               if (is_null_oid(&orig_head) && !is_cache_unborn())
                        die(_("Updating an unborn branch with changes added to the index."));
 
                if (!autostash)
-                       die_on_unclean_work_tree(prefix);
+                       require_clean_work_tree(N_("pull with rebase"),
+                               _("please commit or stash them."), 1, 0);
 
-               if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
-                       hashclr(rebase_fork_point);
+               if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
+                       oidclr(&rebase_fork_point);
        }
 
        if (run_fetch(repo, refspecs))
@@ -887,11 +861,11 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (opt_dry_run)
                return 0;
 
-       if (get_sha1("HEAD", curr_head))
-               hashclr(curr_head);
+       if (get_oid("HEAD", &curr_head))
+               oidclr(&curr_head);
 
-       if (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) &&
-                       hashcmp(orig_head, curr_head)) {
+       if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
+                       oidcmp(&orig_head, &curr_head)) {
                /*
                 * The fetch involved updating the current branch.
                 *
@@ -902,15 +876,15 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 
                warning(_("fetch updated the current branch head.\n"
                        "fast-forwarding your working tree from\n"
-                       "commit %s."), sha1_to_hex(orig_head));
+                       "commit %s."), oid_to_hex(&orig_head));
 
-               if (checkout_fast_forward(orig_head, curr_head, 0))
+               if (checkout_fast_forward(&orig_head, &curr_head, 0))
                        die(_("Cannot fast-forward your working tree.\n"
                                "After making sure that you saved anything precious from\n"
                                "$ git diff %s\n"
                                "output, run\n"
                                "$ git reset --hard\n"
-                               "to recover."), sha1_to_hex(orig_head));
+                               "to recover."), oid_to_hex(&orig_head));
        }
 
        get_merge_heads(&merge_heads);
@@ -918,14 +892,45 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (!merge_heads.nr)
                die_no_merge_candidates(repo, refspecs);
 
-       if (is_null_sha1(orig_head)) {
+       if (is_null_oid(&orig_head)) {
                if (merge_heads.nr > 1)
                        die(_("Cannot merge multiple branches into empty head."));
-               return pull_into_void(*merge_heads.sha1, curr_head);
-       } else if (opt_rebase) {
-               if (merge_heads.nr > 1)
-                       die(_("Cannot rebase onto multiple branches."));
-               return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);
-       } else
-               return run_merge();
+               return pull_into_void(merge_heads.oid, &curr_head);
+       }
+       if (opt_rebase && merge_heads.nr > 1)
+               die(_("Cannot rebase onto multiple branches."));
+
+       if (opt_rebase) {
+               int ret = 0;
+               if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
+                    recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
+                   submodule_touches_in_range(&rebase_fork_point, &curr_head))
+                       die(_("cannot rebase with locally recorded submodule modifications"));
+               if (!autostash) {
+                       struct commit_list *list = NULL;
+                       struct commit *merge_head, *head;
+
+                       head = lookup_commit_reference(&orig_head);
+                       commit_list_insert(head, &list);
+                       merge_head = lookup_commit_reference(&merge_heads.oid[0]);
+                       if (is_descendant_of(merge_head, list)) {
+                               /* we can fast-forward this without invoking rebase */
+                               opt_ff = "--ff-only";
+                               ret = run_merge();
+                       }
+               }
+               ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+
+               if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+                            recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+                       ret = rebase_submodules();
+
+               return ret;
+       } else {
+               int ret = run_merge();
+               if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+                            recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+                       ret = update_submodules();
+               return ret;
+       }
 }