branch.con commit remote-hg: allow refs with spaces (b0f6c58)
   1#include "cache.h"
   2#include "branch.h"
   3#include "refs.h"
   4#include "remote.h"
   5#include "commit.h"
   6
   7struct tracking {
   8        struct refspec spec;
   9        char *src;
  10        const char *remote;
  11        int matches;
  12};
  13
  14static int find_tracked_branch(struct remote *remote, void *priv)
  15{
  16        struct tracking *tracking = priv;
  17
  18        if (!remote_find_tracking(remote, &tracking->spec)) {
  19                if (++tracking->matches == 1) {
  20                        tracking->src = tracking->spec.src;
  21                        tracking->remote = remote->name;
  22                } else {
  23                        free(tracking->spec.src);
  24                        if (tracking->src) {
  25                                free(tracking->src);
  26                                tracking->src = NULL;
  27                        }
  28                }
  29                tracking->spec.src = NULL;
  30        }
  31
  32        return 0;
  33}
  34
  35static int should_setup_rebase(const char *origin)
  36{
  37        switch (autorebase) {
  38        case AUTOREBASE_NEVER:
  39                return 0;
  40        case AUTOREBASE_LOCAL:
  41                return origin == NULL;
  42        case AUTOREBASE_REMOTE:
  43                return origin != NULL;
  44        case AUTOREBASE_ALWAYS:
  45                return 1;
  46        }
  47        return 0;
  48}
  49
  50void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
  51{
  52        const char *shortname = remote + 11;
  53        int remote_is_branch = !prefixcmp(remote, "refs/heads/");
  54        struct strbuf key = STRBUF_INIT;
  55        int rebasing = should_setup_rebase(origin);
  56
  57        if (remote_is_branch
  58            && !strcmp(local, shortname)
  59            && !origin) {
  60                warning(_("Not setting branch %s as its own upstream."),
  61                        local);
  62                return;
  63        }
  64
  65        strbuf_addf(&key, "branch.%s.remote", local);
  66        git_config_set(key.buf, origin ? origin : ".");
  67
  68        strbuf_reset(&key);
  69        strbuf_addf(&key, "branch.%s.merge", local);
  70        git_config_set(key.buf, remote);
  71
  72        if (rebasing) {
  73                strbuf_reset(&key);
  74                strbuf_addf(&key, "branch.%s.rebase", local);
  75                git_config_set(key.buf, "true");
  76        }
  77        strbuf_release(&key);
  78
  79        if (flag & BRANCH_CONFIG_VERBOSE) {
  80                if (remote_is_branch && origin)
  81                        printf_ln(rebasing ?
  82                                  _("Branch %s set up to track remote branch %s from %s by rebasing.") :
  83                                  _("Branch %s set up to track remote branch %s from %s."),
  84                                  local, shortname, origin);
  85                else if (remote_is_branch && !origin)
  86                        printf_ln(rebasing ?
  87                                  _("Branch %s set up to track local branch %s by rebasing.") :
  88                                  _("Branch %s set up to track local branch %s."),
  89                                  local, shortname);
  90                else if (!remote_is_branch && origin)
  91                        printf_ln(rebasing ?
  92                                  _("Branch %s set up to track remote ref %s by rebasing.") :
  93                                  _("Branch %s set up to track remote ref %s."),
  94                                  local, remote);
  95                else if (!remote_is_branch && !origin)
  96                        printf_ln(rebasing ?
  97                                  _("Branch %s set up to track local ref %s by rebasing.") :
  98                                  _("Branch %s set up to track local ref %s."),
  99                                  local, remote);
 100                else
 101                        die("BUG: impossible combination of %d and %p",
 102                            remote_is_branch, origin);
 103        }
 104}
 105
 106/*
 107 * This is called when new_ref is branched off of orig_ref, and tries
 108 * to infer the settings for branch.<new_ref>.{remote,merge} from the
 109 * config.
 110 */
 111static int setup_tracking(const char *new_ref, const char *orig_ref,
 112                          enum branch_track track, int quiet)
 113{
 114        struct tracking tracking;
 115        int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
 116
 117        if (strlen(new_ref) > 1024 - 7 - 7 - 1)
 118                return error(_("Tracking not set up: name too long: %s"),
 119                                new_ref);
 120
 121        memset(&tracking, 0, sizeof(tracking));
 122        tracking.spec.dst = (char *)orig_ref;
 123        if (for_each_remote(find_tracked_branch, &tracking))
 124                return 1;
 125
 126        if (!tracking.matches)
 127                switch (track) {
 128                case BRANCH_TRACK_ALWAYS:
 129                case BRANCH_TRACK_EXPLICIT:
 130                case BRANCH_TRACK_OVERRIDE:
 131                        break;
 132                default:
 133                        return 1;
 134                }
 135
 136        if (tracking.matches > 1)
 137                return error(_("Not tracking: ambiguous information for ref %s"),
 138                                orig_ref);
 139
 140        install_branch_config(config_flags, new_ref, tracking.remote,
 141                              tracking.src ? tracking.src : orig_ref);
 142
 143        free(tracking.src);
 144        return 0;
 145}
 146
 147struct branch_desc_cb {
 148        const char *config_name;
 149        const char *value;
 150};
 151
 152static int read_branch_desc_cb(const char *var, const char *value, void *cb)
 153{
 154        struct branch_desc_cb *desc = cb;
 155        if (strcmp(desc->config_name, var))
 156                return 0;
 157        free((char *)desc->value);
 158        return git_config_string(&desc->value, var, value);
 159}
 160
 161int read_branch_desc(struct strbuf *buf, const char *branch_name)
 162{
 163        struct branch_desc_cb cb;
 164        struct strbuf name = STRBUF_INIT;
 165        strbuf_addf(&name, "branch.%s.description", branch_name);
 166        cb.config_name = name.buf;
 167        cb.value = NULL;
 168        if (git_config(read_branch_desc_cb, &cb) < 0) {
 169                strbuf_release(&name);
 170                return -1;
 171        }
 172        if (cb.value)
 173                strbuf_addstr(buf, cb.value);
 174        strbuf_release(&name);
 175        return 0;
 176}
 177
 178int validate_new_branchname(const char *name, struct strbuf *ref,
 179                            int force, int attr_only)
 180{
 181        if (strbuf_check_branch_ref(ref, name))
 182                die(_("'%s' is not a valid branch name."), name);
 183
 184        if (!ref_exists(ref->buf))
 185                return 0;
 186        else if (!force && !attr_only)
 187                die(_("A branch named '%s' already exists."), ref->buf + strlen("refs/heads/"));
 188
 189        if (!attr_only) {
 190                const char *head;
 191                unsigned char sha1[20];
 192
 193                head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
 194                if (!is_bare_repository() && head && !strcmp(head, ref->buf))
 195                        die(_("Cannot force update the current branch."));
 196        }
 197        return 1;
 198}
 199
 200static const char upstream_not_branch[] =
 201N_("Cannot setup tracking information; starting point '%s' is not a branch.");
 202static const char upstream_missing[] =
 203N_("the requested upstream branch '%s' does not exist");
 204static const char upstream_advice[] =
 205N_("\n"
 206"If you are planning on basing your work on an upstream\n"
 207"branch that already exists at the remote, you may need to\n"
 208"run \"git fetch\" to retrieve it.\n"
 209"\n"
 210"If you are planning to push out a new local branch that\n"
 211"will track its remote counterpart, you may want to use\n"
 212"\"git push -u\" to set the upstream config as you push.");
 213
 214void create_branch(const char *head,
 215                   const char *name, const char *start_name,
 216                   int force, int reflog, int clobber_head,
 217                   int quiet, enum branch_track track)
 218{
 219        struct ref_lock *lock = NULL;
 220        struct commit *commit;
 221        unsigned char sha1[20];
 222        char *real_ref, msg[PATH_MAX + 20];
 223        struct strbuf ref = STRBUF_INIT;
 224        int forcing = 0;
 225        int dont_change_ref = 0;
 226        int explicit_tracking = 0;
 227
 228        if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE)
 229                explicit_tracking = 1;
 230
 231        if (validate_new_branchname(name, &ref, force,
 232                                    track == BRANCH_TRACK_OVERRIDE ||
 233                                    clobber_head)) {
 234                if (!force)
 235                        dont_change_ref = 1;
 236                else
 237                        forcing = 1;
 238        }
 239
 240        real_ref = NULL;
 241        if (get_sha1(start_name, sha1)) {
 242                if (explicit_tracking) {
 243                        if (advice_set_upstream_failure) {
 244                                error(_(upstream_missing), start_name);
 245                                advise(_(upstream_advice));
 246                                exit(1);
 247                        }
 248                        die(_(upstream_missing), start_name);
 249                }
 250                die(_("Not a valid object name: '%s'."), start_name);
 251        }
 252
 253        switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
 254        case 0:
 255                /* Not branching from any existing branch */
 256                if (explicit_tracking)
 257                        die(_(upstream_not_branch), start_name);
 258                break;
 259        case 1:
 260                /* Unique completion -- good, only if it is a real branch */
 261                if (prefixcmp(real_ref, "refs/heads/") &&
 262                    prefixcmp(real_ref, "refs/remotes/")) {
 263                        if (explicit_tracking)
 264                                die(_(upstream_not_branch), start_name);
 265                        else
 266                                real_ref = NULL;
 267                }
 268                break;
 269        default:
 270                die(_("Ambiguous object name: '%s'."), start_name);
 271                break;
 272        }
 273
 274        if ((commit = lookup_commit_reference(sha1)) == NULL)
 275                die(_("Not a valid branch point: '%s'."), start_name);
 276        hashcpy(sha1, commit->object.sha1);
 277
 278        if (!dont_change_ref) {
 279                lock = lock_any_ref_for_update(ref.buf, NULL, 0);
 280                if (!lock)
 281                        die_errno(_("Failed to lock ref for update"));
 282        }
 283
 284        if (reflog)
 285                log_all_ref_updates = 1;
 286
 287        if (forcing)
 288                snprintf(msg, sizeof msg, "branch: Reset to %s",
 289                         start_name);
 290        else if (!dont_change_ref)
 291                snprintf(msg, sizeof msg, "branch: Created from %s",
 292                         start_name);
 293
 294        if (real_ref && track)
 295                setup_tracking(ref.buf+11, real_ref, track, quiet);
 296
 297        if (!dont_change_ref)
 298                if (write_ref_sha1(lock, sha1, msg) < 0)
 299                        die_errno(_("Failed to write ref"));
 300
 301        strbuf_release(&ref);
 302        free(real_ref);
 303}
 304
 305void remove_branch_state(void)
 306{
 307        unlink(git_path("CHERRY_PICK_HEAD"));
 308        unlink(git_path("REVERT_HEAD"));
 309        unlink(git_path("MERGE_HEAD"));
 310        unlink(git_path("MERGE_RR"));
 311        unlink(git_path("MERGE_MSG"));
 312        unlink(git_path("MERGE_MODE"));
 313        unlink(git_path("SQUASH_MSG"));
 314}