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