Merge branch 'jc/unused-symbols'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Apr 2017 06:29:27 +0000 (23:29 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Apr 2017 06:29:27 +0000 (23:29 -0700)
Code cleanup.

* jc/unused-symbols:
remote.[ch]: parse_push_cas_option() can be static

1  2 
remote.c
remote.h
diff --combined remote.c
index 9f83fe2c4cbd477e47fc6c81eab324be7aa088c6,fc2df99b8100db62afbdfaa115c75ef50fe8ea20..af72727d760d37e511ba662148029c5d0eb52259
+++ b/remote.c
@@@ -8,7 -8,6 +8,7 @@@
  #include "tag.h"
  #include "string-list.h"
  #include "mergesort.h"
 +#include "argv-array.h"
  
  enum map_direction { FROM_SRC, FROM_DST };
  
@@@ -50,11 -49,17 +50,11 @@@ static int branches_alloc
  static int branches_nr;
  
  static struct branch *current_branch;
 -static const char *default_remote_name;
 -static const char *branch_pushremote_name;
  static const char *pushremote_name;
 -static int explicit_default_remote_name;
  
  static struct rewrites rewrites;
  static struct rewrites rewrites_push;
  
 -#define BUF_SIZE (2048)
 -static char buffer[BUF_SIZE];
 -
  static int valid_remote(const struct remote *remote)
  {
        return (!!remote->url) || (!!remote->foreign_vcs);
@@@ -63,6 -68,7 +63,6 @@@
  static const char *alias_url(const char *url, struct rewrites *r)
  {
        int i, j;
 -      char *ret;
        struct counted_string *longest;
        int longest_i;
  
        if (!longest)
                return url;
  
 -      ret = xmalloc(r->rewrite[longest_i]->baselen +
 -                   (strlen(url) - longest->len) + 1);
 -      strcpy(ret, r->rewrite[longest_i]->base);
 -      strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
 -      return ret;
 +      return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
  }
  
  static void add_push_refspec(struct remote *remote, const char *ref)
@@@ -241,169 -251,206 +241,169 @@@ static void add_instead_of(struct rewri
        rewrite->instead_of_nr++;
  }
  
 +static const char *skip_spaces(const char *s)
 +{
 +      while (isspace(*s))
 +              s++;
 +      return s;
 +}
 +
  static void read_remotes_file(struct remote *remote)
  {
 +      struct strbuf buf = STRBUF_INIT;
        FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
  
        if (!f)
                return;
 +      remote->configured_in_repo = 1;
        remote->origin = REMOTE_REMOTES;
 -      while (fgets(buffer, BUF_SIZE, f)) {
 -              int value_list;
 -              char *s, *p;
 -
 -              if (starts_with(buffer, "URL:")) {
 -                      value_list = 0;
 -                      s = buffer + 4;
 -              } else if (starts_with(buffer, "Push:")) {
 -                      value_list = 1;
 -                      s = buffer + 5;
 -              } else if (starts_with(buffer, "Pull:")) {
 -                      value_list = 2;
 -                      s = buffer + 5;
 -              } else
 -                      continue;
 -
 -              while (isspace(*s))
 -                      s++;
 -              if (!*s)
 -                      continue;
 +      while (strbuf_getline(&buf, f) != EOF) {
 +              const char *v;
  
 -              p = s + strlen(s);
 -              while (isspace(p[-1]))
 -                      *--p = 0;
 +              strbuf_rtrim(&buf);
  
 -              switch (value_list) {
 -              case 0:
 -                      add_url_alias(remote, xstrdup(s));
 -                      break;
 -              case 1:
 -                      add_push_refspec(remote, xstrdup(s));
 -                      break;
 -              case 2:
 -                      add_fetch_refspec(remote, xstrdup(s));
 -                      break;
 -              }
 +              if (skip_prefix(buf.buf, "URL:", &v))
 +                      add_url_alias(remote, xstrdup(skip_spaces(v)));
 +              else if (skip_prefix(buf.buf, "Push:", &v))
 +                      add_push_refspec(remote, xstrdup(skip_spaces(v)));
 +              else if (skip_prefix(buf.buf, "Pull:", &v))
 +                      add_fetch_refspec(remote, xstrdup(skip_spaces(v)));
        }
 +      strbuf_release(&buf);
        fclose(f);
  }
  
  static void read_branches_file(struct remote *remote)
  {
        char *frag;
 -      struct strbuf branch = STRBUF_INIT;
 -      int n = 1000;
 -      FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
 -      char *s, *p;
 -      int len;
 +      struct strbuf buf = STRBUF_INIT;
 +      FILE *f = fopen(git_path("branches/%s", remote->name), "r");
  
        if (!f)
                return;
 -      s = fgets(buffer, BUF_SIZE, f);
 +
 +      strbuf_getline_lf(&buf, f);
        fclose(f);
 -      if (!s)
 -              return;
 -      while (isspace(*s))
 -              s++;
 -      if (!*s)
 +      strbuf_trim(&buf);
 +      if (!buf.len) {
 +              strbuf_release(&buf);
                return;
 +      }
 +
 +      remote->configured_in_repo = 1;
        remote->origin = REMOTE_BRANCHES;
 -      p = s + strlen(s);
 -      while (isspace(p[-1]))
 -              *--p = 0;
 -      len = p - s;
 -      p = xmalloc(len + 1);
 -      strcpy(p, s);
  
        /*
         * The branches file would have URL and optionally
         * #branch specified.  The "master" (or specified) branch is
 -       * fetched and stored in the local branch of the same name.
 +       * fetched and stored in the local branch matching the
 +       * remote name.
         */
 -      frag = strchr(p, '#');
 -      if (frag) {
 +      frag = strchr(buf.buf, '#');
 +      if (frag)
                *(frag++) = '\0';
 -              strbuf_addf(&branch, "refs/heads/%s", frag);
 -      } else
 -              strbuf_addstr(&branch, "refs/heads/master");
 +      else
 +              frag = "master";
 +
 +      add_url_alias(remote, strbuf_detach(&buf, NULL));
 +      add_fetch_refspec(remote, xstrfmt("refs/heads/%s:refs/heads/%s",
 +                                        frag, remote->name));
  
 -      strbuf_addf(&branch, ":refs/heads/%s", remote->name);
 -      add_url_alias(remote, p);
 -      add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
        /*
         * Cogito compatible push: push current HEAD to remote #branch
         * (master if missing)
         */
 -      strbuf_init(&branch, 0);
 -      strbuf_addstr(&branch, "HEAD");
 -      if (frag)
 -              strbuf_addf(&branch, ":refs/heads/%s", frag);
 -      else
 -              strbuf_addstr(&branch, ":refs/heads/master");
 -      add_push_refspec(remote, strbuf_detach(&branch, NULL));
 +      add_push_refspec(remote, xstrfmt("HEAD:refs/heads/%s", frag));
        remote->fetch_tags = 1; /* always auto-follow */
  }
  
  static int handle_config(const char *key, const char *value, void *cb)
  {
        const char *name;
 +      int namelen;
        const char *subkey;
        struct remote *remote;
        struct branch *branch;
 -      if (starts_with(key, "branch.")) {
 -              name = key + 7;
 -              subkey = strrchr(name, '.');
 -              if (!subkey)
 +      if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
 +              if (!name)
                        return 0;
 -              branch = make_branch(name, subkey - name);
 -              if (!strcmp(subkey, ".remote")) {
 -                      if (git_config_string(&branch->remote_name, key, value))
 -                              return -1;
 -                      if (branch == current_branch) {
 -                              default_remote_name = branch->remote_name;
 -                              explicit_default_remote_name = 1;
 -                      }
 -              } else if (!strcmp(subkey, ".pushremote")) {
 -                      if (branch == current_branch)
 -                              if (git_config_string(&branch_pushremote_name, key, value))
 -                                      return -1;
 -              } else if (!strcmp(subkey, ".merge")) {
 +              branch = make_branch(name, namelen);
 +              if (!strcmp(subkey, "remote")) {
 +                      return git_config_string(&branch->remote_name, key, value);
 +              } else if (!strcmp(subkey, "pushremote")) {
 +                      return git_config_string(&branch->pushremote_name, key, value);
 +              } else if (!strcmp(subkey, "merge")) {
                        if (!value)
                                return config_error_nonbool(key);
                        add_merge(branch, xstrdup(value));
                }
                return 0;
        }
 -      if (starts_with(key, "url.")) {
 +      if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) {
                struct rewrite *rewrite;
 -              name = key + 4;
 -              subkey = strrchr(name, '.');
 -              if (!subkey)
 +              if (!name)
                        return 0;
 -              if (!strcmp(subkey, ".insteadof")) {
 -                      rewrite = make_rewrite(&rewrites, name, subkey - name);
 +              if (!strcmp(subkey, "insteadof")) {
 +                      rewrite = make_rewrite(&rewrites, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
 -              } else if (!strcmp(subkey, ".pushinsteadof")) {
 -                      rewrite = make_rewrite(&rewrites_push, name, subkey - name);
 +              } else if (!strcmp(subkey, "pushinsteadof")) {
 +                      rewrite = make_rewrite(&rewrites_push, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
                }
        }
  
 -      if (!starts_with(key,  "remote."))
 +      if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0)
                return 0;
 -      name = key + 7;
  
        /* Handle remote.* variables */
 -      if (!strcmp(name, "pushdefault"))
 +      if (!name && !strcmp(subkey, "pushdefault"))
                return git_config_string(&pushremote_name, key, value);
  
 +      if (!name)
 +              return 0;
        /* Handle remote.<name>.* variables */
        if (*name == '/') {
                warning("Config remote shorthand cannot begin with '/': %s",
                        name);
                return 0;
        }
 -      subkey = strrchr(name, '.');
 -      if (!subkey)
 -              return 0;
 -      remote = make_remote(name, subkey - name);
 +      remote = make_remote(name, namelen);
        remote->origin = REMOTE_CONFIG;
 -      if (!strcmp(subkey, ".mirror"))
 +      if (current_config_scope() == CONFIG_SCOPE_REPO)
 +              remote->configured_in_repo = 1;
 +      if (!strcmp(subkey, "mirror"))
                remote->mirror = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".skipdefaultupdate"))
 +      else if (!strcmp(subkey, "skipdefaultupdate"))
                remote->skip_default_update = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".skipfetchall"))
 +      else if (!strcmp(subkey, "skipfetchall"))
                remote->skip_default_update = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".prune"))
 +      else if (!strcmp(subkey, "prune"))
                remote->prune = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".url")) {
 +      else if (!strcmp(subkey, "url")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_url(remote, v);
 -      } else if (!strcmp(subkey, ".pushurl")) {
 +      } else if (!strcmp(subkey, "pushurl")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_pushurl(remote, v);
 -      } else if (!strcmp(subkey, ".push")) {
 +      } else if (!strcmp(subkey, "push")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_push_refspec(remote, v);
 -      } else if (!strcmp(subkey, ".fetch")) {
 +      } else if (!strcmp(subkey, "fetch")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_fetch_refspec(remote, v);
 -      } else if (!strcmp(subkey, ".receivepack")) {
 +      } else if (!strcmp(subkey, "receivepack")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                        remote->receivepack = v;
                else
                        error("more than one receivepack given, using the first");
 -      } else if (!strcmp(subkey, ".uploadpack")) {
 +      } else if (!strcmp(subkey, "uploadpack")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                        remote->uploadpack = v;
                else
                        error("more than one uploadpack given, using the first");
 -      } else if (!strcmp(subkey, ".tagopt")) {
 +      } else if (!strcmp(subkey, "tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
                else if (!strcmp(value, "--tags"))
                        remote->fetch_tags = 2;
 -      } else if (!strcmp(subkey, ".proxy")) {
 +      } else if (!strcmp(subkey, "proxy")) {
                return git_config_string((const char **)&remote->http_proxy,
                                         key, value);
 -      } else if (!strcmp(subkey, ".vcs")) {
 +      } else if (!strcmp(subkey, "proxyauthmethod")) {
 +              return git_config_string((const char **)&remote->http_proxy_authmethod,
 +                                       key, value);
 +      } else if (!strcmp(subkey, "vcs")) {
                return git_config_string(&remote->foreign_vcs, key, value);
        }
        return 0;
@@@ -457,23 -501,23 +457,23 @@@ static void alias_all_urls(void
  
  static void read_config(void)
  {
 -      unsigned char sha1[20];
 -      const char *head_ref;
 +      static int loaded;
 +      struct object_id oid;
        int flag;
 -      if (default_remote_name) /* did this already */
 +
 +      if (loaded)
                return;
 -      default_remote_name = "origin";
 +      loaded = 1;
 +
        current_branch = NULL;
 -      head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
 -      if (head_ref && (flag & REF_ISSYMREF) &&
 -          skip_prefix(head_ref, "refs/heads/", &head_ref)) {
 -              current_branch = make_branch(head_ref, 0);
 +      if (startup_info->have_repository) {
 +              const char *head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
 +              if (head_ref && (flag & REF_ISSYMREF) &&
 +                  skip_prefix(head_ref, "refs/heads/", &head_ref)) {
 +                      current_branch = make_branch(head_ref, 0);
 +              }
        }
        git_config(handle_config, NULL);
 -      if (branch_pushremote_name) {
 -              free((char *)pushremote_name);
 -              pushremote_name = branch_pushremote_name;
 -      }
        alias_all_urls();
  }
  
@@@ -547,12 -591,12 +547,12 @@@ static struct refspec *parse_refspec_in
                flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
  
                if (fetch) {
 -                      unsigned char unused[40];
 +                      struct object_id unused;
  
                        /* LHS */
                        if (!*rs[i].src)
                                ; /* empty is ok; it means "HEAD" */
 -                      else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
 +                      else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))
                                rs[i].exact_sha1 = 1; /* ok */
                        else if (!check_refname_format(rs[i].src, flags))
                                ; /* valid looking ref is ok */
@@@ -652,48 -696,25 +652,48 @@@ static int valid_remote_nick(const cha
        return !strchr(name, '/'); /* no slash */
  }
  
 -static struct remote *remote_get_1(const char *name, const char *pushremote_name)
 +const char *remote_for_branch(struct branch *branch, int *explicit)
 +{
 +      if (branch && branch->remote_name) {
 +              if (explicit)
 +                      *explicit = 1;
 +              return branch->remote_name;
 +      }
 +      if (explicit)
 +              *explicit = 0;
 +      return "origin";
 +}
 +
 +const char *pushremote_for_branch(struct branch *branch, int *explicit)
 +{
 +      if (branch && branch->pushremote_name) {
 +              if (explicit)
 +                      *explicit = 1;
 +              return branch->pushremote_name;
 +      }
 +      if (pushremote_name) {
 +              if (explicit)
 +                      *explicit = 1;
 +              return pushremote_name;
 +      }
 +      return remote_for_branch(branch, explicit);
 +}
 +
 +static struct remote *remote_get_1(const char *name,
 +                                 const char *(*get_default)(struct branch *, int *))
  {
        struct remote *ret;
        int name_given = 0;
  
 +      read_config();
 +
        if (name)
                name_given = 1;
 -      else {
 -              if (pushremote_name) {
 -                      name = pushremote_name;
 -                      name_given = 1;
 -              } else {
 -                      name = default_remote_name;
 -                      name_given = explicit_default_remote_name;
 -              }
 -      }
 +      else
 +              name = get_default(current_branch, &name_given);
  
        ret = make_remote(name, 0);
 -      if (valid_remote_nick(name)) {
 +      if (valid_remote_nick(name) && have_git_dir()) {
                if (!valid_remote(ret))
                        read_remotes_file(ret);
                if (!valid_remote(ret))
  
  struct remote *remote_get(const char *name)
  {
 -      read_config();
 -      return remote_get_1(name, NULL);
 +      return remote_get_1(name, remote_for_branch);
  }
  
  struct remote *pushremote_get(const char *name)
  {
 -      read_config();
 -      return remote_get_1(name, pushremote_name);
 +      return remote_get_1(name, pushremote_for_branch);
  }
  
 -int remote_is_configured(const char *name)
 +int remote_is_configured(struct remote *remote, int in_repo)
  {
 -      struct remotes_hash_key lookup;
 -      struct hashmap_entry lookup_entry;
 -      read_config();
 -
 -      init_remotes_hash();
 -      lookup.str = name;
 -      lookup.len = strlen(name);
 -      hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
 -
 -      return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
 +      if (!remote)
 +              return 0;
 +      if (in_repo)
 +              return remote->configured_in_repo;
 +      return !!remote->origin;
  }
  
  int for_each_remote(each_remote_fn fn, void *priv)
@@@ -926,7 -954,7 +926,7 @@@ static struct ref *alloc_ref_with_prefi
                const char *name)
  {
        size_t len = strlen(name);
 -      struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
 +      struct ref *ref = xcalloc(1, st_add4(sizeof(*ref), prefixlen, len, 1));
        memcpy(ref->name, prefix, prefixlen);
        memcpy(ref->name + prefixlen, name, len);
        return ref;
@@@ -943,9 -971,9 +943,9 @@@ struct ref *copy_ref(const struct ref *
        size_t len;
        if (!ref)
                return NULL;
 -      len = strlen(ref->name);
 -      cpy = xmalloc(sizeof(struct ref) + len + 1);
 -      memcpy(cpy, ref, sizeof(struct ref) + len + 1);
 +      len = st_add3(sizeof(struct ref), strlen(ref->name), 1);
 +      cpy = xmalloc(len);
 +      memcpy(cpy, ref, len);
        cpy->next = NULL;
        cpy->symref = xstrdup_or_null(ref->symref);
        cpy->remote_status = xstrdup_or_null(ref->remote_status);
@@@ -1073,14 -1101,14 +1073,14 @@@ static void tail_link_ref(struct ref *r
  static struct ref *alloc_delete_ref(void)
  {
        struct ref *ref = alloc_ref("(delete)");
 -      hashclr(ref->new_sha1);
 +      oidclr(&ref->new_oid);
        return ref;
  }
  
  static int try_explicit_object_name(const char *name,
                                    struct ref **match)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        if (!*name) {
                if (match)
                return 0;
        }
  
 -      if (get_sha1(name, sha1))
 +      if (get_sha1(name, oid.hash))
                return -1;
  
        if (match) {
                *match = alloc_ref(name);
 -              hashcpy((*match)->new_sha1, sha1);
 +              oidcpy(&(*match)->new_oid, &oid);
        }
        return 0;
  }
@@@ -1108,10 -1136,10 +1108,10 @@@ static struct ref *make_linked_ref(cons
  static char *guess_ref(const char *name, struct ref *peer)
  {
        struct strbuf buf = STRBUF_INIT;
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
 -                                         sha1, NULL);
 +                                         oid.hash, NULL);
        if (!r)
                return NULL;
  
@@@ -1169,12 -1197,12 +1169,12 @@@ static int match_explicit(struct ref *s
                return -1;
  
        if (!dst_value) {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                int flag;
  
                dst_value = resolve_ref_unsafe(matched_src->name,
                                               RESOLVE_REF_READING,
 -                                             sha1, &flag);
 +                                             oid.hash, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
                     !starts_with(dst_value, "refs/heads/")))
        case 0:
                if (starts_with(dst_value, "refs/"))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
 -              else if (is_null_sha1(matched_src->new_sha1))
 +              else if (is_null_oid(&matched_src->new_oid))
                        error("unable to delete '%s': remote ref does not exist",
                              dst_value);
                else if ((dst_guess = guess_ref(dst_value, matched_src)))
@@@ -1290,13 -1318,13 +1290,13 @@@ struct tips 
        int nr, alloc;
  };
  
 -static void add_to_tips(struct tips *tips, const unsigned char *sha1)
 +static void add_to_tips(struct tips *tips, const struct object_id *oid)
  {
        struct commit *commit;
  
 -      if (is_null_sha1(sha1))
 +      if (is_null_oid(oid))
                return;
 -      commit = lookup_commit_reference_gently(sha1, 1);
 +      commit = lookup_commit_reference_gently(oid->hash, 1);
        if (!commit || (commit->object.flags & TMP_MARK))
                return;
        commit->object.flags |= TMP_MARK;
@@@ -1319,10 -1347,10 +1319,10 @@@ static void add_missing_tags(struct re
        memset(&sent_tips, 0, sizeof(sent_tips));
        for (ref = *dst; ref; ref = ref->next) {
                if (ref->peer_ref &&
 -                  !is_null_sha1(ref->peer_ref->new_sha1))
 -                      add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
 +                  !is_null_oid(&ref->peer_ref->new_oid))
 +                      add_to_tips(&sent_tips, &ref->peer_ref->new_oid);
                else
 -                      add_to_tips(&sent_tips, ref->old_sha1);
 +                      add_to_tips(&sent_tips, &ref->old_oid);
                if (starts_with(ref->name, "refs/tags/"))
                        string_list_append(&dst_tag, ref->name);
        }
                        continue; /* not a tag */
                if (string_list_has_string(&dst_tag, ref->name))
                        continue; /* they already have it */
 -              if (sha1_object_info(ref->new_sha1, NULL) != OBJ_TAG)
 +              if (sha1_object_info(ref->new_oid.hash, NULL) != OBJ_TAG)
                        continue; /* be conservative */
                item = string_list_append(&src_tag, ref->name);
                item->util = ref;
                        struct ref *dst_ref;
                        struct commit *commit;
  
 -                      if (is_null_sha1(ref->new_sha1))
 +                      if (is_null_oid(&ref->new_oid))
                                continue;
 -                      commit = lookup_commit_reference_gently(ref->new_sha1, 1);
 +                      commit = lookup_commit_reference_gently(ref->new_oid.hash, 1);
                        if (!commit)
                                /* not pushing a commit, which is not an error */
                                continue;
  
                        /* Add it in */
                        dst_ref = make_linked_ref(ref->name, dst_tail);
 -                      hashcpy(dst_ref->new_sha1, ref->new_sha1);
 +                      oidcpy(&dst_ref->new_oid, &ref->new_oid);
                        dst_ref->peer_ref = copy_ref(ref);
                }
        }
@@@ -1479,7 -1507,7 +1479,7 @@@ int match_push_refs(struct ref *src, st
  
                        /* Create a new one and link it */
                        dst_peer = make_linked_ref(dst_name, &dst_tail);
 -                      hashcpy(dst_peer->new_sha1, ref->new_sha1);
 +                      oidcpy(&dst_peer->new_oid, &ref->new_oid);
                        string_list_insert(&dst_ref_index,
                                dst_peer->name)->util = dst_peer;
                }
@@@ -1531,20 -1559,23 +1531,20 @@@ void set_ref_status_for_push(struct re
                int reject_reason = 0;
  
                if (ref->peer_ref)
 -                      hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 +                      oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
                else if (!send_mirror)
                        continue;
  
 -              ref->deletion = is_null_sha1(ref->new_sha1);
 +              ref->deletion = is_null_oid(&ref->new_oid);
                if (!ref->deletion &&
 -                      !hashcmp(ref->old_sha1, ref->new_sha1)) {
 +                      !oidcmp(&ref->old_oid, &ref->new_oid)) {
                        ref->status = REF_STATUS_UPTODATE;
                        continue;
                }
  
                /*
 -               * Bypass the usual "must fast-forward" check but
 -               * replace it with a weaker "the old value must be
 -               * this value we observed".  If the remote ref has
 -               * moved and is now different from what we expect,
 -               * reject any push.
 +               * If the remote ref has moved and is now different
 +               * from what we expect, reject any push.
                 *
                 * It also is an error if the user told us to check
                 * with the remote-tracking branch to find the value
                 * branch.
                 */
                if (ref->expect_old_sha1) {
 -                      if (ref->expect_old_no_trackback ||
 -                          hashcmp(ref->old_sha1, ref->old_sha1_expect))
 +                      if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
 +                      else
 +                              /* If the ref isn't stale then force the update. */
 +                              force_ref_update = 1;
                }
  
                /*
 -               * The usual "must fast-forward" rules.
 +               * If the update isn't already rejected then check
 +               * the usual "must fast-forward" rules.
                 *
                 * Decide whether an individual refspec A:B can be
                 * pushed.  The push will succeed if any of the
                 *     passing the --force argument
                 */
  
 -              else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
 +              if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
                        if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
 -                      else if (!has_sha1_file(ref->old_sha1))
 +                      else if (!has_object_file(&ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
 -                      else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
 -                               !lookup_commit_reference_gently(ref->new_sha1, 1))
 +                      else if (!lookup_commit_reference_gently(ref->old_oid.hash, 1) ||
 +                               !lookup_commit_reference_gently(ref->new_oid.hash, 1))
                                reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
 -                      else if (!ref_newer(ref->new_sha1, ref->old_sha1))
 +                      else if (!ref_newer(&ref->new_oid, &ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
                }
  
  
  static void set_merge(struct branch *ret)
  {
 +      struct remote *remote;
        char *ref;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int i;
  
 +      if (!ret)
 +              return; /* no branch */
 +      if (ret->merge)
 +              return; /* already run */
 +      if (!ret->remote_name || !ret->merge_nr) {
 +              /*
 +               * no merge config; let's make sure we don't confuse callers
 +               * with a non-zero merge_nr but a NULL merge
 +               */
 +              ret->merge_nr = 0;
 +              return;
 +      }
 +
 +      remote = remote_get(ret->remote_name);
 +
        ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
        for (i = 0; i < ret->merge_nr; i++) {
                ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
                ret->merge[i]->src = xstrdup(ret->merge_name[i]);
 -              if (!remote_find_tracking(ret->remote, ret->merge[i]) ||
 +              if (!remote_find_tracking(remote, ret->merge[i]) ||
                    strcmp(ret->remote_name, "."))
                        continue;
                if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
 -                           sha1, &ref) == 1)
 +                           oid.hash, &ref) == 1)
                        ret->merge[i]->dst = ref;
                else
                        ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
@@@ -1649,7 -1661,11 +1649,7 @@@ struct branch *branch_get(const char *n
                ret = current_branch;
        else
                ret = make_branch(name, 0);
 -      if (ret && ret->remote_name) {
 -              ret->remote = remote_get(ret->remote_name);
 -              if (ret->merge_nr)
 -                      set_merge(ret);
 -      }
 +      set_merge(ret);
        return ret;
  }
  
@@@ -1667,136 -1683,12 +1667,136 @@@ int branch_merge_matches(struct branch 
        return refname_match(branch->merge[i]->src, refname);
  }
  
 +__attribute__((format (printf,2,3)))
 +static const char *error_buf(struct strbuf *err, const char *fmt, ...)
 +{
 +      if (err) {
 +              va_list ap;
 +              va_start(ap, fmt);
 +              strbuf_vaddf(err, fmt, ap);
 +              va_end(ap);
 +      }
 +      return NULL;
 +}
 +
 +const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
 +{
 +      if (!branch)
 +              return error_buf(err, _("HEAD does not point to a branch"));
 +
 +      if (!branch->merge || !branch->merge[0]) {
 +              /*
 +               * no merge config; is it because the user didn't define any,
 +               * or because it is not a real branch, and get_branch
 +               * auto-vivified it?
 +               */
 +              if (!ref_exists(branch->refname))
 +                      return error_buf(err, _("no such branch: '%s'"),
 +                                       branch->name);
 +              return error_buf(err,
 +                               _("no upstream configured for branch '%s'"),
 +                               branch->name);
 +      }
 +
 +      if (!branch->merge[0]->dst)
 +              return error_buf(err,
 +                               _("upstream branch '%s' not stored as a remote-tracking branch"),
 +                               branch->merge[0]->src);
 +
 +      return branch->merge[0]->dst;
 +}
 +
 +static const char *tracking_for_push_dest(struct remote *remote,
 +                                        const char *refname,
 +                                        struct strbuf *err)
 +{
 +      char *ret;
 +
 +      ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
 +      if (!ret)
 +              return error_buf(err,
 +                               _("push destination '%s' on remote '%s' has no local tracking branch"),
 +                               refname, remote->name);
 +      return ret;
 +}
 +
 +static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
 +{
 +      struct remote *remote;
 +
 +      remote = remote_get(pushremote_for_branch(branch, NULL));
 +      if (!remote)
 +              return error_buf(err,
 +                               _("branch '%s' has no remote for pushing"),
 +                               branch->name);
 +
 +      if (remote->push_refspec_nr) {
 +              char *dst;
 +              const char *ret;
 +
 +              dst = apply_refspecs(remote->push, remote->push_refspec_nr,
 +                                   branch->refname);
 +              if (!dst)
 +                      return error_buf(err,
 +                                       _("push refspecs for '%s' do not include '%s'"),
 +                                       remote->name, branch->name);
 +
 +              ret = tracking_for_push_dest(remote, dst, err);
 +              free(dst);
 +              return ret;
 +      }
 +
 +      if (remote->mirror)
 +              return tracking_for_push_dest(remote, branch->refname, err);
 +
 +      switch (push_default) {
 +      case PUSH_DEFAULT_NOTHING:
 +              return error_buf(err, _("push has no destination (push.default is 'nothing')"));
 +
 +      case PUSH_DEFAULT_MATCHING:
 +      case PUSH_DEFAULT_CURRENT:
 +              return tracking_for_push_dest(remote, branch->refname, err);
 +
 +      case PUSH_DEFAULT_UPSTREAM:
 +              return branch_get_upstream(branch, err);
 +
 +      case PUSH_DEFAULT_UNSPECIFIED:
 +      case PUSH_DEFAULT_SIMPLE:
 +              {
 +                      const char *up, *cur;
 +
 +                      up = branch_get_upstream(branch, err);
 +                      if (!up)
 +                              return NULL;
 +                      cur = tracking_for_push_dest(remote, branch->refname, err);
 +                      if (!cur)
 +                              return NULL;
 +                      if (strcmp(cur, up))
 +                              return error_buf(err,
 +                                               _("cannot resolve 'simple' push to a single destination"));
 +                      return cur;
 +              }
 +      }
 +
 +      die("BUG: unhandled push situation");
 +}
 +
 +const char *branch_get_push(struct branch *branch, struct strbuf *err)
 +{
 +      if (!branch)
 +              return error_buf(err, _("HEAD does not point to a branch"));
 +
 +      if (!branch->push_tracking_ref)
 +              branch->push_tracking_ref = branch_get_push_1(branch, err);
 +      return branch->push_tracking_ref;
 +}
 +
  static int ignore_symref_update(const char *refname)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int flag;
  
 -      if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
 +      if (!resolve_ref_unsafe(refname, 0, oid.hash, &flag))
                return 0; /* non-existing refs are OK */
        return (flag & REF_ISSYMREF);
  }
@@@ -1887,7 -1779,7 +1887,7 @@@ int get_fetch_map(const struct ref *rem
  
                if (refspec->exact_sha1) {
                        ref_map = alloc_ref(name);
 -                      get_sha1_hex(name, ref_map->old_sha1);
 +                      get_oid_hex(name, &ref_map->old_oid);
                } else {
                        ref_map = get_remote_ref(remote_refs, name);
                }
@@@ -1928,7 -1820,7 +1928,7 @@@ int resolve_remote_symref(struct ref *r
                return 0;
        for (; list; list = list->next)
                if (!strcmp(ref->symref, list->name)) {
 -                      hashcpy(ref->old_sha1, list->old_sha1);
 +                      oidcpy(&ref->old_oid, &list->old_oid);
                        return 0;
                }
        return 1;
  static void unmark_and_free(struct commit_list *list, unsigned int mark)
  {
        while (list) {
 -              struct commit_list *temp = list;
 -              temp->item->object.flags &= ~mark;
 -              list = temp->next;
 -              free(temp);
 +              struct commit *commit = pop_commit(&list);
 +              commit->object.flags &= ~mark;
        }
  }
  
 -int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
 +int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
  {
        struct object *o;
        struct commit *old, *new;
         * Both new and old must be commit-ish and new is descendant of
         * old.  Otherwise we require --force.
         */
 -      o = deref_tag(parse_object(old_sha1), NULL, 0);
 +      o = deref_tag(parse_object(old_oid->hash), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
        old = (struct commit *) o;
  
 -      o = deref_tag(parse_object(new_sha1), NULL, 0);
 +      o = deref_tag(parse_object(new_oid->hash), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
        new = (struct commit *) o;
  
  /*
   * Compare a branch with its upstream, and save their differences (number
 - * of commits) in *num_ours and *num_theirs.
 + * of commits) in *num_ours and *num_theirs. The name of the upstream branch
 + * (or NULL if no upstream is defined) is returned via *upstream_name, if it
 + * is not itself NULL.
   *
 - * Return 0 if branch has no upstream (no base), -1 if upstream is missing
 - * (with "gone" base), otherwise 1 (with base).
 + * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
 + * upstream defined, or ref does not exist), 0 otherwise.
   */
 -int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
 +int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
 +                     const char **upstream_name)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct commit *ours, *theirs;
 -      char symmetric[84];
        struct rev_info revs;
 -      const char *rev_argv[10], *base;
 -      int rev_argc;
 +      const char *base;
 +      struct argv_array argv = ARGV_ARRAY_INIT;
  
        /* Cannot stat unless we are marked to build on top of somebody else. */
 -      if (!branch ||
 -          !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
 -              return 0;
 +      base = branch_get_upstream(branch, NULL);
 +      if (upstream_name)
 +              *upstream_name = base;
 +      if (!base)
 +              return -1;
  
        /* Cannot stat if what we used to build on no longer exists */
 -      base = branch->merge[0]->dst;
 -      if (read_ref(base, sha1))
 +      if (read_ref(base, oid.hash))
                return -1;
 -      theirs = lookup_commit_reference(sha1);
 +      theirs = lookup_commit_reference(oid.hash);
        if (!theirs)
                return -1;
  
 -      if (read_ref(branch->refname, sha1))
 +      if (read_ref(branch->refname, oid.hash))
                return -1;
 -      ours = lookup_commit_reference(sha1);
 +      ours = lookup_commit_reference(oid.hash);
        if (!ours)
                return -1;
  
        /* are we the same? */
        if (theirs == ours) {
                *num_theirs = *num_ours = 0;
 -              return 1;
 +              return 0;
        }
  
        /* Run "rev-list --left-right ours...theirs" internally... */
 -      rev_argc = 0;
 -      rev_argv[rev_argc++] = NULL;
 -      rev_argv[rev_argc++] = "--left-right";
 -      rev_argv[rev_argc++] = symmetric;
 -      rev_argv[rev_argc++] = "--";
 -      rev_argv[rev_argc] = NULL;
 -
 -      strcpy(symmetric, sha1_to_hex(ours->object.sha1));
 -      strcpy(symmetric + 40, "...");
 -      strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
 +      argv_array_push(&argv, ""); /* ignored */
 +      argv_array_push(&argv, "--left-right");
 +      argv_array_pushf(&argv, "%s...%s",
 +                       oid_to_hex(&ours->object.oid),
 +                       oid_to_hex(&theirs->object.oid));
 +      argv_array_push(&argv, "--");
  
        init_revisions(&revs, NULL);
 -      setup_revisions(rev_argc, rev_argv, &revs, NULL);
 +      setup_revisions(argv.argc, argv.argv, &revs, NULL);
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
  
        /* clear object flags smudged by the above traversal */
        clear_commit_marks(ours, ALL_REV_FLAGS);
        clear_commit_marks(theirs, ALL_REV_FLAGS);
 -      return 1;
 +
 +      argv_array_clear(&argv);
 +      return 0;
  }
  
  /*
  int format_tracking_info(struct branch *branch, struct strbuf *sb)
  {
        int ours, theirs;
 +      const char *full_base;
        char *base;
        int upstream_is_gone = 0;
  
 -      switch (stat_tracking_info(branch, &ours, &theirs)) {
 -      case 0:
 -              /* no base */
 -              return 0;
 -      case -1:
 -              /* with "gone" base */
 +      if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {
 +              if (!full_base)
 +                      return 0;
                upstream_is_gone = 1;
 -              break;
 -      default:
 -              /* with base */
 -              break;
        }
  
 -      base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
 +      base = shorten_unambiguous_ref(full_base, 0);
        if (upstream_is_gone) {
                strbuf_addf(sb,
                        _("Your branch is based on '%s', but the upstream is gone.\n"),
                        base);
                if (advice_status_hints)
 -                      strbuf_addf(sb,
 +                      strbuf_addstr(sb,
                                _("  (use \"git branch --unset-upstream\" to fixup)\n"));
        } else if (!ours && !theirs) {
                strbuf_addf(sb,
                           ours),
                        base, ours);
                if (advice_status_hints)
 -                      strbuf_addf(sb,
 +                      strbuf_addstr(sb,
                                _("  (use \"git push\" to publish your local commits)\n"));
        } else if (!ours) {
                strbuf_addf(sb,
                           theirs),
                        base, theirs);
                if (advice_status_hints)
 -                      strbuf_addf(sb,
 +                      strbuf_addstr(sb,
                                _("  (use \"git pull\" to update your local branch)\n"));
        } else {
                strbuf_addf(sb,
                           "Your branch and '%s' have diverged,\n"
                               "and have %d and %d different commits each, "
                               "respectively.\n",
 -                         theirs),
 +                         ours + theirs),
                        base, ours, theirs);
                if (advice_status_hints)
 -                      strbuf_addf(sb,
 +                      strbuf_addstr(sb,
                                _("  (use \"git pull\" to merge the remote branch into yours)\n"));
        }
        free(base);
        return 1;
  }
  
 -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int one_local_ref(const char *refname, const struct object_id *oid,
 +                       int flag, void *cb_data)
  {
        struct ref ***local_tail = cb_data;
        struct ref *ref;
 -      int len;
  
        /* we already know it starts with refs/ to get here */
        if (check_refname_format(refname + 5, 0))
                return 0;
  
 -      len = strlen(refname) + 1;
 -      ref = xcalloc(1, sizeof(*ref) + len);
 -      hashcpy(ref->new_sha1, sha1);
 -      memcpy(ref->name, refname, len);
 +      ref = alloc_ref(refname);
 +      oidcpy(&ref->new_oid, oid);
        **local_tail = ref;
        *local_tail = &ref->next;
        return 0;
  struct ref *get_local_heads(void)
  {
        struct ref *local_refs = NULL, **local_tail = &local_refs;
 +
        for_each_ref(one_local_ref, &local_tail);
        return local_refs;
  }
@@@ -2172,7 -2072,7 +2172,7 @@@ struct ref *guess_remote_head(const str
        /* If refs/heads/master could be right, it is. */
        if (!all) {
                r = find_ref_by_name(refs, "refs/heads/master");
 -              if (r && !hashcmp(r->old_sha1, head->old_sha1))
 +              if (r && !oidcmp(&r->old_oid, &head->old_oid))
                        return copy_ref(r);
        }
  
        for (r = refs; r; r = r->next) {
                if (r != head &&
                    starts_with(r->name, "refs/heads/") &&
 -                  !hashcmp(r->old_sha1, head->old_sha1)) {
 +                  !oidcmp(&r->old_oid, &head->old_oid)) {
                        *tail = copy_ref(r);
                        tail = &((*tail)->next);
                        if (!all)
@@@ -2198,8 -2098,8 +2198,8 @@@ struct stale_heads_info 
        int ref_count;
  };
  
 -static int get_stale_heads_cb(const char *refname,
 -      const unsigned char *sha1, int flags, void *cb_data)
 +static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
 +                            int flags, void *cb_data)
  {
        struct stale_heads_info *info = cb_data;
        struct string_list matches = STRING_LIST_INIT_DUP;
  
        if (stale) {
                struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
 -              hashcpy(ref->new_sha1, sha1);
 +              oidcpy(&ref->new_oid, oid);
        }
  
  clean_exit:
@@@ -2241,7 -2141,6 +2241,7 @@@ struct ref *get_stale_heads(struct refs
        struct ref *ref, *stale_refs = NULL;
        struct string_list ref_names = STRING_LIST_INIT_NODUP;
        struct stale_heads_info info;
 +
        info.ref_names = &ref_names;
        info.stale_refs_tail = &stale_refs;
        info.refs = refs;
@@@ -2279,7 -2178,7 +2279,7 @@@ static struct push_cas *add_cas_entry(s
        return entry;
  }
  
- int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
  {
        const char *colon;
        struct push_cas *entry;
        entry = add_cas_entry(cas, arg, colon - arg);
        if (!*colon)
                entry->use_tracking = 1;
 +      else if (!colon[1])
 +              hashclr(entry->expect);
        else if (get_sha1(colon + 1, entry->expect))
                return error("cannot parse expected object name '%s'", colon + 1);
        return 0;
@@@ -2325,14 -2222,14 +2325,14 @@@ int is_empty_cas(const struct push_cas_
   * If we cannot do so, return negative to signal an error.
   */
  static int remote_tracking(struct remote *remote, const char *refname,
 -                         unsigned char sha1[20])
 +                         struct object_id *oid)
  {
        char *dst;
  
        dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
        if (!dst)
                return -1; /* no tracking ref for refname at remote */
 -      if (read_ref(dst, sha1))
 +      if (read_ref(dst, oid->hash))
                return -1; /* we know what the tracking ref is but we cannot read it */
        return 0;
  }
@@@ -2350,9 -2247,9 +2350,9 @@@ static void apply_cas(struct push_cas_o
                        continue;
                ref->expect_old_sha1 = 1;
                if (!entry->use_tracking)
 -                      hashcpy(ref->old_sha1_expect, cas->entry[i].expect);
 -              else if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
 -                      ref->expect_old_no_trackback = 1;
 +                      hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
 +              else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
 +                      oidclr(&ref->old_oid_expect);
                return;
        }
  
                return;
  
        ref->expect_old_sha1 = 1;
 -      if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
 -              ref->expect_old_no_trackback = 1;
 +      if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
 +              oidclr(&ref->old_oid_expect);
  }
  
  void apply_push_cas(struct push_cas_option *cas,
diff --combined remote.h
index dd8c5175776baabbac2bd735e21bc859fb62a111,19a4905da6631dc9911ccbda18a969c234bdc88d..d7992c4ef192fb7fbdf644f0b836290411505c82
+++ b/remote.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "hashmap.h"
  
  enum {
 +      REMOTE_UNCONFIGURED = 0,
        REMOTE_CONFIG,
        REMOTE_REMOTES,
        REMOTE_BRANCHES
@@@ -15,7 -14,7 +15,7 @@@ struct remote 
        struct hashmap_entry ent;  /* must be first */
  
        const char *name;
 -      int origin;
 +      int origin, configured_in_repo;
  
        const char *foreign_vcs;
  
         * for curl remotes only
         */
        char *http_proxy;
 +      char *http_proxy_authmethod;
  };
  
  struct remote *remote_get(const char *name);
  struct remote *pushremote_get(const char *name);
 -int remote_is_configured(const char *name);
 +int remote_is_configured(struct remote *remote, int in_repo);
  
  typedef int each_remote_fn(struct remote *remote, void *priv);
  int for_each_remote(each_remote_fn fn, void *priv);
@@@ -81,21 -79,17 +81,21 @@@ extern const struct refspec *tag_refspe
  
  struct ref {
        struct ref *next;
 -      unsigned char old_sha1[20];
 -      unsigned char new_sha1[20];
 -      unsigned char old_sha1_expect[20]; /* used by expect-old */
 +      struct object_id old_oid;
 +      struct object_id new_oid;
 +      struct object_id old_oid_expect; /* used by expect-old */
        char *symref;
        unsigned int
                force:1,
                forced_update:1,
                expect_old_sha1:1,
 -              expect_old_no_trackback:1,
 -              deletion:1,
 -              matched:1;
 +              deletion:1;
 +
 +      enum {
 +              REF_NOT_MATCHED = 0, /* initial value */
 +              REF_MATCHED,
 +              REF_UNADVERTISED_NOT_ALLOWED
 +      } match_status;
  
        /*
         * Order is important here, as we write to FETCH_HEAD
@@@ -156,7 -150,7 +156,7 @@@ extern struct ref **get_remote_heads(in
                                     struct sha1_array *shallow);
  
  int resolve_remote_symref(struct ref *ref, struct ref *list);
 -int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
 +int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
  
  /*
   * Remove and free all but the first of any entries in the input list
@@@ -209,42 -203,19 +209,42 @@@ struct branch 
        const char *refname;
  
        const char *remote_name;
 -      struct remote *remote;
 +      const char *pushremote_name;
  
        const char **merge_name;
        struct refspec **merge;
        int merge_nr;
        int merge_alloc;
 +
 +      const char *push_tracking_ref;
  };
  
  struct branch *branch_get(const char *name);
 +const char *remote_for_branch(struct branch *branch, int *explicit);
 +const char *pushremote_for_branch(struct branch *branch, int *explicit);
  
  int branch_has_merge_config(struct branch *branch);
  int branch_merge_matches(struct branch *, int n, const char *);
  
 +/**
 + * Return the fully-qualified refname of the tracking branch for `branch`.
 + * I.e., what "branch@{upstream}" would give you. Returns NULL if no
 + * upstream is defined.
 + *
 + * If `err` is not NULL and no upstream is defined, a more specific error
 + * message is recorded there (if the function does not return NULL, then
 + * `err` is not touched).
 + */
 +const char *branch_get_upstream(struct branch *branch, struct strbuf *err);
 +
 +/**
 + * Return the tracking branch that corresponds to the ref we would push to
 + * given a bare `git push` while `branch` is checked out.
 + *
 + * The return value and `err` conventions match those of `branch_get_upstream`.
 + */
 +const char *branch_get_push(struct branch *branch, struct strbuf *err);
 +
  /* Flags to match_refs. */
  enum match_refs_flags {
        MATCH_REFS_NONE         = 0,
  };
  
  /* Reporting of tracking info */
 -int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
 +int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
 +                     const char **upstream_name);
  int format_tracking_info(struct branch *branch, struct strbuf *sb);
  
  struct ref *get_local_heads(void);
@@@ -290,7 -260,6 +290,6 @@@ struct push_cas_option 
  };
  
  extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
- extern int parse_push_cas_option(struct push_cas_option *, const char *arg, int unset);
  
  extern int is_empty_cas(const struct push_cas_option *);
  void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);