Merge branch 'ep/varscope'
authorJunio C Hamano <gitster@pobox.com>
Thu, 27 Feb 2014 22:01:30 +0000 (14:01 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 27 Feb 2014 22:01:30 +0000 (14:01 -0800)
Shrink lifetime of variables by moving their definitions to an
inner scope where appropriate.

* ep/varscope:
builtin/gc.c: reduce scope of variables
builtin/fetch.c: reduce scope of variable
builtin/commit.c: reduce scope of variables
builtin/clean.c: reduce scope of variable
builtin/blame.c: reduce scope of variables
builtin/apply.c: reduce scope of variables
bisect.c: reduce scope of variable

1  2 
bisect.c
builtin/apply.c
builtin/blame.c
builtin/clean.c
builtin/commit.c
builtin/fetch.c
builtin/gc.c
diff --combined bisect.c
index 37200b41f1dce3359c491039aeebea0108597833,c718249a822656df29cb7ecdb10bbd8884cef61e..8448d27877e3d77fcae01b1354d192121e050528
+++ b/bisect.c
@@@ -406,9 -406,9 +406,9 @@@ static int register_ref(const char *ref
        if (!strcmp(refname, "bad")) {
                current_bad_sha1 = xmalloc(20);
                hashcpy(current_bad_sha1, sha1);
 -      } else if (!prefixcmp(refname, "good-")) {
 +      } else if (starts_with(refname, "good-")) {
                sha1_array_append(&good_revs, sha1);
 -      } else if (!prefixcmp(refname, "skip-")) {
 +      } else if (starts_with(refname, "skip-")) {
                sha1_array_append(&skipped_revs, sha1);
        }
  
@@@ -685,7 -685,6 +685,6 @@@ static void mark_expected_rev(char *bis
  
  static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
  {
-       int res;
  
        mark_expected_rev(bisect_rev_hex);
  
                        die("update-ref --no-deref HEAD failed on %s",
                            bisect_rev_hex);
        } else {
+               int res;
                res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
                if (res)
                        exit(res);
diff --combined builtin/apply.c
index b0d0986226ccb7fa06b08bacba6269b8834e8dd6,154c63fcbbaafb25bfb9cc06f4a4aa21d7982152..a7e72d57ab0d23ef3becb3fd7d4564d6050efe6e
@@@ -1409,10 -1409,10 +1409,10 @@@ static void recount_diff(const char *li
                case '\\':
                        continue;
                case '@':
 -                      ret = size < 3 || prefixcmp(line, "@@ ");
 +                      ret = size < 3 || !starts_with(line, "@@ ");
                        break;
                case 'd':
 -                      ret = size < 5 || prefixcmp(line, "diff ");
 +                      ret = size < 5 || !starts_with(line, "diff ");
                        break;
                default:
                        ret = -1;
@@@ -1798,11 -1798,11 +1798,11 @@@ static struct fragment *parse_binary_hu
  
        *status_p = 0;
  
 -      if (!prefixcmp(buffer, "delta ")) {
 +      if (starts_with(buffer, "delta ")) {
                patch_method = BINARY_DELTA_DEFLATED;
                origlen = strtoul(buffer + 6, NULL, 10);
        }
 -      else if (!prefixcmp(buffer, "literal ")) {
 +      else if (starts_with(buffer, "literal ")) {
                patch_method = BINARY_LITERAL_DEFLATED;
                origlen = strtoul(buffer + 8, NULL, 10);
        }
@@@ -1943,13 -1943,7 +1943,7 @@@ static int parse_chunk(char *buffer, un
                                       size - offset - hdrsize, patch);
  
        if (!patchsize) {
-               static const char *binhdr[] = {
-                       "Binary files ",
-                       "Files ",
-                       NULL,
-               };
                static const char git_binary[] = "GIT binary patch\n";
-               int i;
                int hd = hdrsize + offset;
                unsigned long llen = linelen(buffer + hd, size - hd);
  
                                patchsize = 0;
                }
                else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
+                       static const char *binhdr[] = {
+                               "Binary files ",
+                               "Files ",
+                               NULL,
+                       };
+                       int i;
                        for (i = 0; binhdr[i]; i++) {
                                int len = strlen(binhdr[i]);
                                if (len < size - hd &&
@@@ -3627,12 -3627,12 +3627,12 @@@ static int preimage_sha1_in_gitlink_pat
            hunk->oldpos == 1 && hunk->oldlines == 1 &&
            /* does preimage begin with the heading? */
            (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
 -          !prefixcmp(++preimage, heading) &&
 +          starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
            !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
            preimage[sizeof(heading) + 40 - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
 -          !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
 +          starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
                return 0; /* it all looks fine */
  
        /* we may have full object name on the index line */
diff --combined builtin/blame.c
index e44a6bb30a5a5299c6d57150dd2f23454b25891a,862c0a23d1abb8808479f353d860604354879eb3..967a7c6bbd15d939e836a5ce829648b1e89b5889
@@@ -1551,7 -1551,8 +1551,7 @@@ static void assign_blame(struct scorebo
                 */
                origin_incref(suspect);
                commit = suspect->commit;
 -              if (!commit->object.parsed)
 -                      parse_commit(commit);
 +              parse_commit(commit);
                if (reverse ||
                    (!(commit->object.flags & UNINTERESTING) &&
                     !(revs->max_age != -1 && commit->date < revs->max_age)))
@@@ -1580,14 -1581,14 +1580,14 @@@ static const char *format_time(unsigne
                               int show_raw_time)
  {
        static char time_buf[128];
-       const char *time_str;
-       int time_len;
-       int tz;
  
        if (show_raw_time) {
                snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
        }
        else {
+               const char *time_str;
+               int time_len;
+               int tz;
                tz = atoi(tz_str);
                time_str = show_date(time, tz, blame_date_mode);
                time_len = strlen(time_str);
@@@ -1803,17 -1804,17 +1803,17 @@@ static int prepare_lines(struct scorebo
  static int read_ancestry(const char *graft_file)
  {
        FILE *fp = fopen(graft_file, "r");
 -      char buf[1024];
 +      struct strbuf buf = STRBUF_INIT;
        if (!fp)
                return -1;
 -      while (fgets(buf, sizeof(buf), fp)) {
 +      while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
 -              int len = strlen(buf);
 -              struct commit_graft *graft = read_graft_line(buf, len);
 +              struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
                if (graft)
                        register_commit_graft(graft, 0);
        }
        fclose(fp);
 +      strbuf_release(&buf);
        return 0;
  }
  
diff --combined builtin/clean.c
index cb02a5330ac58266ec4bd0414414bc6f08b92ed8,22ad058c79aa504f79073ce8dc1540b402f61afe..114d7bf879690fb1c1c33f768b86776b6ff60ef8
@@@ -100,7 -100,7 +100,7 @@@ static int parse_clean_color_slot(cons
  
  static int git_clean_config(const char *var, const char *value, void *cb)
  {
 -      if (!prefixcmp(var, "column."))
 +      if (starts_with(var, "column."))
                return git_column_config(var, value, "clean", &colopts);
  
        /* honors the color.interactive* config variables which also
                clean_use_color = git_config_colorbool(var, value);
                return 0;
        }
 -      if (!prefixcmp(var, "color.interactive.")) {
 +      if (starts_with(var, "color.interactive.")) {
                int slot = parse_clean_color_slot(var +
                                                  strlen("color.interactive."));
                if (slot < 0)
@@@ -154,7 -154,7 +154,7 @@@ static int remove_dirs(struct strbuf *p
        DIR *dir;
        struct strbuf quoted = STRBUF_INIT;
        struct dirent *e;
-       int res = 0, ret = 0, gone = 1, original_len = path->len, len, i;
+       int res = 0, ret = 0, gone = 1, original_len = path->len, len;
        unsigned char submodule_head[20];
        struct string_list dels = STRING_LIST_INIT_DUP;
  
        }
  
        if (!*dir_gone && !quiet) {
+               int i;
                for (i = 0; i < dels.nr; i++)
                        printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
        }
@@@ -933,18 -934,36 +934,18 @@@ int cmd_clean(int argc, const char **ar
  
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
 -              int len, pos;
                int matches = 0;
 -              const struct cache_entry *ce;
                struct stat st;
                const char *rel;
  
 -              /*
 -               * Remove the '/' at the end that directory
 -               * walking adds for directory entries.
 -               */
 -              len = ent->len;
 -              if (len && ent->name[len-1] == '/')
 -                      len--;
 -              pos = cache_name_pos(ent->name, len);
 -              if (0 <= pos)
 -                      continue;       /* exact match */
 -              pos = -pos - 1;
 -              if (pos < active_nr) {
 -                      ce = active_cache[pos];
 -                      if (ce_namelen(ce) == len &&
 -                          !memcmp(ce->name, ent->name, len))
 -                              continue; /* Yup, this one exists unmerged */
 -              }
 +              if (!cache_name_is_other(ent->name, ent->len))
 +                      continue;
  
                if (lstat(ent->name, &st))
                        die_errno("Cannot lstat '%s'", ent->name);
  
                if (pathspec.nr)
 -                      matches = match_pathspec_depth(&pathspec, ent->name,
 -                                                     len, 0, NULL);
 +                      matches = dir_path_match(ent, &pathspec, 0, NULL);
  
                if (S_ISDIR(st.st_mode)) {
                        if (remove_directories || (matches == MATCHED_EXACTLY)) {
diff --combined builtin/commit.c
index 824be655857bf4fa5b1992054ef1f8948835df85,42b32d710e4cc8e1f873dee96c7e5dd1744f3271..3783bcadcd5232ceccab7f4fd2559a9999c53ffa
@@@ -234,7 -234,7 +234,7 @@@ static int list_paths(struct string_lis
  
                if (ce->ce_flags & CE_UPDATE)
                        continue;
 -              if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
 +              if (!ce_path_match(ce, pattern, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@@ -307,7 -307,6 +307,6 @@@ static char *prepare_index(int argc, co
        int fd;
        struct string_list partial;
        struct pathspec pathspec;
-       char *old_index_env = NULL;
        int refresh_flags = REFRESH_QUIET;
  
        if (is_status)
                die(_("index file corrupt"));
  
        if (interactive) {
+               char *old_index_env = NULL;
                fd = hold_locked_index(&index_lock, 1);
  
                refresh_cache_or_die(refresh_flags);
@@@ -600,12 -600,10 +600,10 @@@ static int prepare_to_commit(const cha
  {
        struct stat statbuf;
        struct strbuf committer_ident = STRBUF_INIT;
-       int commitable, saved_color_setting;
+       int commitable;
        struct strbuf sb = STRBUF_INIT;
-       char *buffer;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
-       int ident_shown = 0;
        int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
        int old_display_comment_prefix;
  
                                  logfile);
                hook_arg1 = "message";
        } else if (use_message) {
+               char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
                if (!use_editor && (!buffer || buffer[2] == '\0'))
                        die(_("commit has empty message"));
                                eol = nl - sb.buf;
                        else
                                eol = sb.len;
 -                      if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
 +                      if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
                                ignore_footer = sb.len - previous;
                                break;
                        }
        /* This checks if committer ident is explicitly given */
        strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT));
        if (use_editor && include_status) {
+               int ident_shown = 0;
+               int saved_color_setting;
                char *ai_tmp, *ci_tmp;
                if (whence != FROM_COMMIT)
                        status_printf_ln(s, GIT_COLOR_NORMAL,
@@@ -904,7 -905,7 +905,7 @@@ static int rest_is_empty(struct strbuf 
                        eol = sb->len;
  
                if (strlen(sign_off_header) <= eol - i &&
 -                  !prefixcmp(sb->buf + i, sign_off_header)) {
 +                  starts_with(sb->buf + i, sign_off_header)) {
                        i = eol;
                        continue;
                }
@@@ -1183,7 -1184,7 +1184,7 @@@ static int git_status_config(const cha
  {
        struct wt_status *s = cb;
  
 -      if (!prefixcmp(k, "column."))
 +      if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
                s->display_comment_prefix = git_config_bool(k, v);
                return 0;
        }
 -      if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
 +      if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                if (slot < 0)
                        return 0;
@@@ -1338,7 -1339,7 +1339,7 @@@ static void print_summary(const char *p
        commit = lookup_commit(sha1);
        if (!commit)
                die(_("couldn't look up newly created commit"));
 -      if (!commit || parse_commit(commit))
 +      if (parse_commit(commit))
                die(_("could not parse newly created commit"));
  
        strbuf_addstr(&format, "format:%h] %s");
  
        head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
        printf("[%s%s ",
 -              !prefixcmp(head, "refs/heads/") ?
 +              starts_with(head, "refs/heads/") ?
                        head + 11 :
                        !strcmp(head, "HEAD") ?
                                _("detached HEAD") :
@@@ -1406,10 -1407,6 +1407,10 @@@ static int git_commit_config(const cha
        }
        if (!strcmp(k, "commit.cleanup"))
                return git_config_string(&cleanup_arg, k, v);
 +      if (!strcmp(k, "commit.gpgsign")) {
 +              sign_commit = git_config_bool(k, v) ? "" : NULL;
 +              return 0;
 +      }
  
        status = git_gpg_config(k, v, NULL);
        if (status)
@@@ -1509,12 -1506,11 +1510,11 @@@ int cmd_commit(int argc, const char **a
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
 -      char *nl, *p;
 +      char *nl;
        unsigned char sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
        struct stat statbuf;
-       int allow_fast_forward = 1;
        struct commit *current_head = NULL;
        struct commit_extra_header *extra = NULL;
  
                current_head = NULL;
        else {
                current_head = lookup_commit_or_die(sha1, "HEAD");
 -              if (!current_head || parse_commit(current_head))
 +              if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
        argc = parse_and_validate_options(argc, argv, builtin_commit_options,
        } else if (whence == FROM_MERGE) {
                struct strbuf m = STRBUF_INIT;
                FILE *fp;
+               int allow_fast_forward = 1;
  
                if (!reflog_msg)
                        reflog_msg = "commit (merge)";
        }
  
        /* Truncate the message just before the diff, if any. */
 -      if (verbose) {
 -              p = strstr(sb.buf, "\ndiff --git ");
 -              if (p != NULL)
 -                      strbuf_setlen(&sb, p - sb.buf + 1);
 -      }
 +      if (verbose)
 +              wt_status_truncate_message_at_cut_line(&sb);
  
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
diff --combined builtin/fetch.c
index 025bc3e38d7d8055699ea15f241648602cfaf267,256c9eb020e5d3612991d04adac21a6874b9b63f..55f457c04f5c72ca31ab4fbc28e62ea48b6cb471
@@@ -36,7 -36,7 +36,7 @@@ static int prune = -1; /* unspecified *
  
  static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
  static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 -static int tags = TAGS_DEFAULT, unshallow;
 +static int tags = TAGS_DEFAULT, unshallow, update_shallow;
  static const char *depth;
  static const char *upload_pack;
  static struct strbuf default_rla = STRBUF_INIT;
@@@ -44,7 -44,6 +44,7 @@@ static struct transport *gtransport
  static struct transport *gsecondary;
  static const char *submodule_prefix = "";
  static const char *recurse_submodules_default;
 +static int shown_url = 0;
  
  static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
@@@ -105,8 -104,6 +105,8 @@@ static struct option builtin_fetch_opti
        { OPTION_STRING, 0, "recurse-submodules-default",
                   &recurse_submodules_default, NULL,
                   N_("default mode for recursion"), PARSE_OPT_HIDDEN },
 +      OPT_BOOL(0, "update-shallow", &update_shallow,
 +               N_("accept refs that update .git/shallow")),
        OPT_END()
  };
  
@@@ -163,156 -160,48 +163,156 @@@ static void add_merge_config(struct re
        }
  }
  
 +static int add_existing(const char *refname, const unsigned char *sha1,
 +                      int flag, void *cbdata)
 +{
 +      struct string_list *list = (struct string_list *)cbdata;
 +      struct string_list_item *item = string_list_insert(list, refname);
 +      item->util = xmalloc(20);
 +      hashcpy(item->util, sha1);
 +      return 0;
 +}
 +
 +static int will_fetch(struct ref **head, const unsigned char *sha1)
 +{
 +      struct ref *rm = *head;
 +      while (rm) {
 +              if (!hashcmp(rm->old_sha1, sha1))
 +                      return 1;
 +              rm = rm->next;
 +      }
 +      return 0;
 +}
 +
  static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
 -                      struct ref ***tail);
 +                      struct ref ***tail)
 +{
 +      struct string_list existing_refs = STRING_LIST_INIT_DUP;
 +      struct string_list remote_refs = STRING_LIST_INIT_NODUP;
 +      const struct ref *ref;
 +      struct string_list_item *item = NULL;
 +
 +      for_each_ref(add_existing, &existing_refs);
 +      for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
 +              if (!starts_with(ref->name, "refs/tags/"))
 +                      continue;
 +
 +              /*
 +               * The peeled ref always follows the matching base
 +               * ref, so if we see a peeled ref that we don't want
 +               * to fetch then we can mark the ref entry in the list
 +               * as one to ignore by setting util to NULL.
 +               */
 +              if (ends_with(ref->name, "^{}")) {
 +                      if (item && !has_sha1_file(ref->old_sha1) &&
 +                          !will_fetch(head, ref->old_sha1) &&
 +                          !has_sha1_file(item->util) &&
 +                          !will_fetch(head, item->util))
 +                              item->util = NULL;
 +                      item = NULL;
 +                      continue;
 +              }
 +
 +              /*
 +               * If item is non-NULL here, then we previously saw a
 +               * ref not followed by a peeled reference, so we need
 +               * to check if it is a lightweight tag that we want to
 +               * fetch.
 +               */
 +              if (item && !has_sha1_file(item->util) &&
 +                  !will_fetch(head, item->util))
 +                      item->util = NULL;
 +
 +              item = NULL;
 +
 +              /* skip duplicates and refs that we already have */
 +              if (string_list_has_string(&remote_refs, ref->name) ||
 +                  string_list_has_string(&existing_refs, ref->name))
 +                      continue;
 +
 +              item = string_list_insert(&remote_refs, ref->name);
 +              item->util = (void *)ref->old_sha1;
 +      }
 +      string_list_clear(&existing_refs, 1);
 +
 +      /*
 +       * We may have a final lightweight tag that needs to be
 +       * checked to see if it needs fetching.
 +       */
 +      if (item && !has_sha1_file(item->util) &&
 +          !will_fetch(head, item->util))
 +              item->util = NULL;
 +
 +      /*
 +       * For all the tags in the remote_refs string list,
 +       * add them to the list of refs to be fetched
 +       */
 +      for_each_string_list_item(item, &remote_refs) {
 +              /* Unless we have already decided to ignore this item... */
 +              if (item->util)
 +              {
 +                      struct ref *rm = alloc_ref(item->string);
 +                      rm->peer_ref = alloc_ref(item->string);
 +                      hashcpy(rm->old_sha1, item->util);
 +                      **tail = rm;
 +                      *tail = &rm->next;
 +              }
 +      }
 +
 +      string_list_clear(&remote_refs, 0);
 +}
  
  static struct ref *get_ref_map(struct transport *transport,
 -                             struct refspec *refs, int ref_count, int tags,
 -                             int *autotags)
 +                             struct refspec *refspecs, int refspec_count,
 +                             int tags, int *autotags)
  {
        int i;
        struct ref *rm;
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
  
 -      const struct ref *remote_refs = transport_get_remote_refs(transport);
 +      /* opportunistically-updated references: */
 +      struct ref *orefs = NULL, **oref_tail = &orefs;
  
 -      if (ref_count || tags == TAGS_SET) {
 -              struct ref **old_tail;
 +      const struct ref *remote_refs = transport_get_remote_refs(transport);
  
 -              for (i = 0; i < ref_count; i++) {
 -                      get_fetch_map(remote_refs, &refs[i], &tail, 0);
 -                      if (refs[i].dst && refs[i].dst[0])
 +      if (refspec_count) {
 +              for (i = 0; i < refspec_count; i++) {
 +                      get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
 +                      if (refspecs[i].dst && refspecs[i].dst[0])
                                *autotags = 1;
                }
 -              /* Merge everything on the command line, but not --tags */
 +              /* Merge everything on the command line (but not --tags) */
                for (rm = ref_map; rm; rm = rm->next)
                        rm->fetch_head_status = FETCH_HEAD_MERGE;
 -              if (tags == TAGS_SET)
 -                      get_fetch_map(remote_refs, tag_refspec, &tail, 0);
  
                /*
 -               * For any refs that we happen to be fetching via command-line
 -               * arguments, take the opportunity to update their configured
 -               * counterparts. However, we do not want to mention these
 -               * entries in FETCH_HEAD at all, as they would simply be
 -               * duplicates of existing entries.
 +               * For any refs that we happen to be fetching via
 +               * command-line arguments, the destination ref might
 +               * have been missing or have been different than the
 +               * remote-tracking ref that would be derived from the
 +               * configured refspec.  In these cases, we want to
 +               * take the opportunity to update their configured
 +               * remote-tracking reference.  However, we do not want
 +               * to mention these entries in FETCH_HEAD at all, as
 +               * they would simply be duplicates of existing
 +               * entries, so we set them FETCH_HEAD_IGNORE below.
 +               *
 +               * We compute these entries now, based only on the
 +               * refspecs specified on the command line.  But we add
 +               * them to the list following the refspecs resulting
 +               * from the tags option so that one of the latter,
 +               * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
 +               * by ref_remove_duplicates() in favor of one of these
 +               * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
 -              old_tail = tail;
                for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
                        get_fetch_map(ref_map, &transport->remote->fetch[i],
 -                                    &tail, 1);
 -              for (rm = *old_tail; rm; rm = rm->next)
 -                      rm->fetch_head_status = FETCH_HEAD_IGNORE;
 +                                    &oref_tail, 1);
 +
 +              if (tags == TAGS_SET)
 +                      get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
                        tail = &ref_map->next;
                }
        }
 -      if (tags == TAGS_DEFAULT && *autotags)
 +
 +      if (tags == TAGS_SET)
 +              /* also fetch all tags */
 +              get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 +      else if (tags == TAGS_DEFAULT && *autotags)
                find_non_local_tags(transport, &ref_map, &tail);
 -      ref_remove_duplicates(ref_map);
  
 -      return ref_map;
 +      /* Now append any refs to be updated opportunistically: */
 +      *tail = orefs;
 +      for (rm = orefs; rm; rm = rm->next) {
 +              rm->fetch_head_status = FETCH_HEAD_IGNORE;
 +              tail = &rm->next;
 +      }
 +
 +      return ref_remove_duplicates(ref_map);
  }
  
  #define STORE_REF_ERROR_OTHER 1
@@@ -434,7 -313,7 +434,7 @@@ static int update_local_ref(struct ref 
        }
  
        if (!is_null_sha1(ref->old_sha1) &&
 -          !prefixcmp(ref->name, "refs/tags/")) {
 +          starts_with(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                 * more likely to follow a standard layout.
                 */
                const char *name = remote_ref ? remote_ref->name : "";
 -              if (!prefixcmp(name, "refs/tags/")) {
 +              if (starts_with(name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
 -              } else if (!prefixcmp(name, "refs/heads/")) {
 +              } else if (starts_with(name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
                } else {
@@@ -526,8 -405,6 +526,8 @@@ static int iterate_ref_map(void *cb_dat
        struct ref **rm = cb_data;
        struct ref *ref = *rm;
  
 +      while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
 +              ref = ref->next;
        if (!ref)
                return -1; /* end of the list */
        *rm = ref->next;
@@@ -540,7 -417,7 +540,7 @@@ static int store_updated_refs(const cha
  {
        FILE *fp;
        struct commit *commit;
 -      int url_len, i, shown_url = 0, rc = 0;
 +      int url_len, i, rc = 0;
        struct strbuf note = STRBUF_INIT;
        const char *what, *kind;
        struct ref *rm;
                        struct ref *ref = NULL;
                        const char *merge_status_marker = "";
  
 +                      if (rm->status == REF_STATUS_REJECT_SHALLOW) {
 +                              if (want_status == FETCH_HEAD_MERGE)
 +                                      warning(_("reject %s because shallow roots are not allowed to be updated"),
 +                                              rm->peer_ref ? rm->peer_ref->name : rm->name);
 +                              continue;
 +                      }
 +
                        commit = lookup_commit_reference_gently(rm->old_sha1, 1);
                        if (!commit)
                                rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
                                kind = "";
                                what = "";
                        }
 -                      else if (!prefixcmp(rm->name, "refs/heads/")) {
 +                      else if (starts_with(rm->name, "refs/heads/")) {
                                kind = "branch";
                                what = rm->name + 11;
                        }
 -                      else if (!prefixcmp(rm->name, "refs/tags/")) {
 +                      else if (starts_with(rm->name, "refs/tags/")) {
                                kind = "tag";
                                what = rm->name + 10;
                        }
 -                      else if (!prefixcmp(rm->name, "refs/remotes/")) {
 +                      else if (starts_with(rm->name, "refs/remotes/")) {
                                kind = "remote-tracking branch";
                                what = rm->name + 13;
                        }
@@@ -720,36 -590,17 +720,36 @@@ static int fetch_refs(struct transport 
        return ret;
  }
  
 -static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
 +static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
 +              const char *raw_url)
  {
 -      int result = 0;
 +      int url_len, i, result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
 +      char *url;
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)")
                : _("   (%s has become dangling)");
  
 +      if (raw_url)
 +              url = transport_anonymize_url(raw_url);
 +      else
 +              url = xstrdup("foreign");
 +
 +      url_len = strlen(url);
 +      for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
 +              ;
 +
 +      url_len = i + 1;
 +      if (4 < i && !strncmp(".git", url + i - 3, 4))
 +              url_len = i - 3;
 +
        for (ref = stale_refs; ref; ref = ref->next) {
                if (!dry_run)
                        result |= delete_ref(ref->name, NULL, 0);
 +              if (verbosity >= 0 && !shown_url) {
 +                      fprintf(stderr, _("From %.*s\n"), url_len, url);
 +                      shown_url = 1;
 +              }
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
                                TRANSPORT_SUMMARY(_("[deleted]")),
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
 +      free(url);
        free_refs(stale_refs);
        return result;
  }
  
 -static int add_existing(const char *refname, const unsigned char *sha1,
 -                      int flag, void *cbdata)
 -{
 -      struct string_list *list = (struct string_list *)cbdata;
 -      struct string_list_item *item = string_list_insert(list, refname);
 -      item->util = xmalloc(20);
 -      hashcpy(item->util, sha1);
 -      return 0;
 -}
 -
 -static int will_fetch(struct ref **head, const unsigned char *sha1)
 -{
 -      struct ref *rm = *head;
 -      while (rm) {
 -              if (!hashcmp(rm->old_sha1, sha1))
 -                      return 1;
 -              rm = rm->next;
 -      }
 -      return 0;
 -}
 -
 -static void find_non_local_tags(struct transport *transport,
 -                      struct ref **head,
 -                      struct ref ***tail)
 -{
 -      struct string_list existing_refs = STRING_LIST_INIT_DUP;
 -      struct string_list remote_refs = STRING_LIST_INIT_NODUP;
 -      const struct ref *ref;
 -      struct string_list_item *item = NULL;
 -
 -      for_each_ref(add_existing, &existing_refs);
 -      for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
 -              if (prefixcmp(ref->name, "refs/tags/"))
 -                      continue;
 -
 -              /*
 -               * The peeled ref always follows the matching base
 -               * ref, so if we see a peeled ref that we don't want
 -               * to fetch then we can mark the ref entry in the list
 -               * as one to ignore by setting util to NULL.
 -               */
 -              if (!suffixcmp(ref->name, "^{}")) {
 -                      if (item && !has_sha1_file(ref->old_sha1) &&
 -                          !will_fetch(head, ref->old_sha1) &&
 -                          !has_sha1_file(item->util) &&
 -                          !will_fetch(head, item->util))
 -                              item->util = NULL;
 -                      item = NULL;
 -                      continue;
 -              }
 -
 -              /*
 -               * If item is non-NULL here, then we previously saw a
 -               * ref not followed by a peeled reference, so we need
 -               * to check if it is a lightweight tag that we want to
 -               * fetch.
 -               */
 -              if (item && !has_sha1_file(item->util) &&
 -                  !will_fetch(head, item->util))
 -                      item->util = NULL;
 -
 -              item = NULL;
 -
 -              /* skip duplicates and refs that we already have */
 -              if (string_list_has_string(&remote_refs, ref->name) ||
 -                  string_list_has_string(&existing_refs, ref->name))
 -                      continue;
 -
 -              item = string_list_insert(&remote_refs, ref->name);
 -              item->util = (void *)ref->old_sha1;
 -      }
 -      string_list_clear(&existing_refs, 1);
 -
 -      /*
 -       * We may have a final lightweight tag that needs to be
 -       * checked to see if it needs fetching.
 -       */
 -      if (item && !has_sha1_file(item->util) &&
 -          !will_fetch(head, item->util))
 -              item->util = NULL;
 -
 -      /*
 -       * For all the tags in the remote_refs string list,
 -       * add them to the list of refs to be fetched
 -       */
 -      for_each_string_list_item(item, &remote_refs) {
 -              /* Unless we have already decided to ignore this item... */
 -              if (item->util)
 -              {
 -                      struct ref *rm = alloc_ref(item->string);
 -                      rm->peer_ref = alloc_ref(item->string);
 -                      hashcpy(rm->old_sha1, item->util);
 -                      **tail = rm;
 -                      *tail = &rm->next;
 -              }
 -      }
 -
 -      string_list_clear(&remote_refs, 0);
 -}
 -
  static void check_not_current_branch(struct ref *ref_map)
  {
        struct branch *current_branch = branch_get(NULL);
@@@ -809,8 -759,6 +809,8 @@@ static struct transport *prepare_transp
                set_option(transport, TRANS_OPT_KEEP, "yes");
        if (depth)
                set_option(transport, TRANS_OPT_DEPTH, depth);
 +      if (update_shallow)
 +              set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        return transport;
  }
  
@@@ -876,26 -824,39 +876,26 @@@ static int do_fetch(struct transport *t
  
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
 -      if (fetch_refs(transport, ref_map)) {
 -              free_refs(ref_map);
 -              retcode = 1;
 -              goto cleanup;
 -      }
        if (prune) {
                /*
 -               * If --tags was specified, pretend that the user gave us
 -               * the canonical tags refspec
 +               * We only prune based on refspecs specified
 +               * explicitly (via command line or configuration); we
 +               * don't care whether --tags was specified.
                 */
 -              if (tags == TAGS_SET) {
 -                      const char *tags_str = "refs/tags/*:refs/tags/*";
 -                      struct refspec *tags_refspec, *refspec;
 -
 -                      /* Copy the refspec and add the tags to it */
 -                      refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
 -                      tags_refspec = parse_fetch_refspec(1, &tags_str);
 -                      memcpy(refspec, refs, ref_count * sizeof(struct refspec));
 -                      memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
 -                      ref_count++;
 -
 -                      prune_refs(refspec, ref_count, ref_map);
 -
 -                      ref_count--;
 -                      /* The rest of the strings belong to fetch_one */
 -                      free_refspec(1, tags_refspec);
 -                      free(refspec);
 -              } else if (ref_count) {
 -                      prune_refs(refs, ref_count, ref_map);
 +              if (ref_count) {
 +                      prune_refs(refs, ref_count, ref_map, transport->url);
                } else {
 -                      prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
 +                      prune_refs(transport->remote->fetch,
 +                                 transport->remote->fetch_refspec_nr,
 +                                 ref_map,
 +                                 transport->url);
                }
        }
 +      if (fetch_refs(transport, ref_map)) {
 +              free_refs(ref_map);
 +              retcode = 1;
 +              goto cleanup;
 +      }
        free_refs(ref_map);
  
        /* if neither --no-tags nor --tags was specified, do automated tag
@@@ -931,7 -892,7 +931,7 @@@ static int get_remote_group(const char 
  {
        struct remote_group_data *g = priv;
  
 -      if (!prefixcmp(key, "remotes.") &&
 +      if (starts_with(key, "remotes.") &&
                        !strcmp(key + 8, g->name)) {
                /* split list by white space */
                int space = strcspn(value, " \t\n");
@@@ -969,8 -930,8 +969,8 @@@ static void add_options_to_argv(struct 
  {
        if (dry_run)
                argv_array_push(argv, "--dry-run");
 -      if (prune > 0)
 -              argv_array_push(argv, "--prune");
 +      if (prune != -1)
 +              argv_array_push(argv, prune ? "--prune" : "--no-prune");
        if (update_head_ok)
                argv_array_push(argv, "--update-head-ok");
        if (force)
@@@ -1026,7 -987,6 +1026,6 @@@ static int fetch_multiple(struct string
  
  static int fetch_one(struct remote *remote, int argc, const char **argv)
  {
-       int i;
        static const char **refs = NULL;
        struct refspec *refspec;
        int ref_nr = 0;
  
        if (argc > 0) {
                int j = 0;
+               int i;
                refs = xcalloc(argc + 1, sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
@@@ -1114,10 -1075,6 +1114,10 @@@ int cmd_fetch(int argc, const char **ar
                }
        }
  
 +      /* no need to be strict, transport_set_option() will validate it again */
 +      if (depth && atoi(depth) < 1)
 +              die(_("depth %s is not a positive number"), depth);
 +
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
                if (recurse_submodules_default) {
                        int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
diff --combined builtin/gc.c
index c19545d49e217400ee1736183c870557b9c9d54b,5369012d93399fc5a806d382bcfa0138046fa818..5bbb5e3cc6e3c75ad41a0a92594006a659459634
@@@ -16,7 -16,6 +16,7 @@@
  #include "run-command.h"
  #include "sigchain.h"
  #include "argv-array.h"
 +#include "commit.h"
  
  #define FAILED_RUN "failed to run %s"
  
@@@ -188,13 -187,12 +188,12 @@@ static int need_to_gc(void
  static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
  {
        static struct lock_file lock;
-       static char locking_host[128];
        char my_host[128];
        struct strbuf sb = STRBUF_INIT;
        struct stat st;
        uintmax_t pid;
        FILE *fp;
-       int fd, should_exit;
+       int fd;
  
        if (pidfile)
                /* already locked */
        fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
+               static char locking_host[128];
+               int should_exit;
                fp = fopen(git_path("gc.pid"), "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =