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