branch.con commit t0002: add test for enter_repo(), non-strict mode (3104120)
   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,
 288                                           sha1, forcing ? NULL : null_sha1,
 289                                           0, msg, &err) ||
 290                    ref_transaction_commit(transaction, &err))
 291                        die("%s", err.buf);
 292                ref_transaction_free(transaction);
 293                strbuf_release(&err);
 294        }
 295
 296        if (real_ref && track)
 297                setup_tracking(ref.buf + 11, real_ref, track, quiet);
 298
 299        strbuf_release(&ref);
 300        free(real_ref);
 301}
 302
 303void remove_branch_state(void)
 304{
 305        unlink(git_path_cherry_pick_head());
 306        unlink(git_path_revert_head());
 307        unlink(git_path_merge_head());
 308        unlink(git_path_merge_rr());
 309        unlink(git_path_merge_msg());
 310        unlink(git_path_merge_mode());
 311        unlink(git_path_squash_msg());
 312}
 313
 314static char *find_linked_symref(const char *symref, const char *branch,
 315                                const char *id)
 316{
 317        struct strbuf sb = STRBUF_INIT;
 318        struct strbuf path = STRBUF_INIT;
 319        struct strbuf gitdir = STRBUF_INIT;
 320        char *existing = NULL;
 321
 322        /*
 323         * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside
 324         * $GIT_DIR so resolve_ref_unsafe() won't work (it uses
 325         * git_path). Parse the ref ourselves.
 326         */
 327        if (id)
 328                strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref);
 329        else
 330                strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref);
 331
 332        if (!strbuf_readlink(&sb, path.buf, 0)) {
 333                if (!starts_with(sb.buf, "refs/") ||
 334                    check_refname_format(sb.buf, 0))
 335                        goto done;
 336        } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
 337            starts_with(sb.buf, "ref:")) {
 338                strbuf_remove(&sb, 0, strlen("ref:"));
 339                strbuf_trim(&sb);
 340        } else
 341                goto done;
 342        if (strcmp(sb.buf, branch))
 343                goto done;
 344        if (id) {
 345                strbuf_reset(&path);
 346                strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
 347                if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
 348                        goto done;
 349                strbuf_rtrim(&gitdir);
 350        } else
 351                strbuf_addstr(&gitdir, get_git_common_dir());
 352        strbuf_strip_suffix(&gitdir, ".git");
 353
 354        existing = strbuf_detach(&gitdir, NULL);
 355done:
 356        strbuf_release(&path);
 357        strbuf_release(&sb);
 358        strbuf_release(&gitdir);
 359
 360        return existing;
 361}
 362
 363char *find_shared_symref(const char *symref, const char *target)
 364{
 365        struct strbuf path = STRBUF_INIT;
 366        DIR *dir;
 367        struct dirent *d;
 368        char *existing;
 369
 370        if ((existing = find_linked_symref(symref, target, NULL)))
 371                return existing;
 372
 373        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
 374        dir = opendir(path.buf);
 375        strbuf_release(&path);
 376        if (!dir)
 377                return NULL;
 378
 379        while ((d = readdir(dir)) != NULL) {
 380                if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
 381                        continue;
 382                existing = find_linked_symref(symref, target, d->d_name);
 383                if (existing)
 384                        goto done;
 385        }
 386done:
 387        closedir(dir);
 388
 389        return existing;
 390}
 391
 392void die_if_checked_out(const char *branch)
 393{
 394        char *existing;
 395
 396        existing = find_shared_symref("HEAD", branch);
 397        if (existing) {
 398                skip_prefix(branch, "refs/heads/", &branch);
 399                die(_("'%s' is already checked out at '%s'"), branch, existing);
 400        }
 401}