branch.con commit git-remote-mediawiki: factoring code between git-remote-mediawiki and Git::Mediawiki (192f7a0)
   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 int check_tracking_branch(struct remote *remote, void *cb_data)
 201{
 202        char *tracking_branch = cb_data;
 203        struct refspec query;
 204        memset(&query, 0, sizeof(struct refspec));
 205        query.dst = tracking_branch;
 206        return !(remote_find_tracking(remote, &query) ||
 207                 prefixcmp(query.src, "refs/heads/"));
 208}
 209
 210static int validate_remote_tracking_branch(char *ref)
 211{
 212        return !for_each_remote(check_tracking_branch, ref);
 213}
 214
 215static const char upstream_not_branch[] =
 216N_("Cannot setup tracking information; starting point '%s' is not a branch.");
 217static const char upstream_missing[] =
 218N_("the requested upstream branch '%s' does not exist");
 219static const char upstream_advice[] =
 220N_("\n"
 221"If you are planning on basing your work on an upstream\n"
 222"branch that already exists at the remote, you may need to\n"
 223"run \"git fetch\" to retrieve it.\n"
 224"\n"
 225"If you are planning to push out a new local branch that\n"
 226"will track its remote counterpart, you may want to use\n"
 227"\"git push -u\" to set the upstream config as you push.");
 228
 229void create_branch(const char *head,
 230                   const char *name, const char *start_name,
 231                   int force, int reflog, int clobber_head,
 232                   int quiet, enum branch_track track)
 233{
 234        struct ref_lock *lock = NULL;
 235        struct commit *commit;
 236        unsigned char sha1[20];
 237        char *real_ref, msg[PATH_MAX + 20];
 238        struct strbuf ref = STRBUF_INIT;
 239        int forcing = 0;
 240        int dont_change_ref = 0;
 241        int explicit_tracking = 0;
 242
 243        if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE)
 244                explicit_tracking = 1;
 245
 246        if (validate_new_branchname(name, &ref, force,
 247                                    track == BRANCH_TRACK_OVERRIDE ||
 248                                    clobber_head)) {
 249                if (!force)
 250                        dont_change_ref = 1;
 251                else
 252                        forcing = 1;
 253        }
 254
 255        real_ref = NULL;
 256        if (get_sha1(start_name, sha1)) {
 257                if (explicit_tracking) {
 258                        if (advice_set_upstream_failure) {
 259                                error(_(upstream_missing), start_name);
 260                                advise(_(upstream_advice));
 261                                exit(1);
 262                        }
 263                        die(_(upstream_missing), start_name);
 264                }
 265                die(_("Not a valid object name: '%s'."), start_name);
 266        }
 267
 268        switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
 269        case 0:
 270                /* Not branching from any existing branch */
 271                if (explicit_tracking)
 272                        die(_(upstream_not_branch), start_name);
 273                break;
 274        case 1:
 275                /* Unique completion -- good, only if it is a real branch */
 276                if (prefixcmp(real_ref, "refs/heads/") &&
 277                    validate_remote_tracking_branch(real_ref)) {
 278                        if (explicit_tracking)
 279                                die(_(upstream_not_branch), start_name);
 280                        else
 281                                real_ref = NULL;
 282                }
 283                break;
 284        default:
 285                die(_("Ambiguous object name: '%s'."), start_name);
 286                break;
 287        }
 288
 289        if ((commit = lookup_commit_reference(sha1)) == NULL)
 290                die(_("Not a valid branch point: '%s'."), start_name);
 291        hashcpy(sha1, commit->object.sha1);
 292
 293        if (!dont_change_ref) {
 294                lock = lock_any_ref_for_update(ref.buf, NULL, 0);
 295                if (!lock)
 296                        die_errno(_("Failed to lock ref for update"));
 297        }
 298
 299        if (reflog)
 300                log_all_ref_updates = 1;
 301
 302        if (forcing)
 303                snprintf(msg, sizeof msg, "branch: Reset to %s",
 304                         start_name);
 305        else if (!dont_change_ref)
 306                snprintf(msg, sizeof msg, "branch: Created from %s",
 307                         start_name);
 308
 309        if (real_ref && track)
 310                setup_tracking(ref.buf+11, real_ref, track, quiet);
 311
 312        if (!dont_change_ref)
 313                if (write_ref_sha1(lock, sha1, msg) < 0)
 314                        die_errno(_("Failed to write ref"));
 315
 316        strbuf_release(&ref);
 317        free(real_ref);
 318}
 319
 320void remove_branch_state(void)
 321{
 322        unlink(git_path("CHERRY_PICK_HEAD"));
 323        unlink(git_path("REVERT_HEAD"));
 324        unlink(git_path("MERGE_HEAD"));
 325        unlink(git_path("MERGE_RR"));
 326        unlink(git_path("MERGE_MSG"));
 327        unlink(git_path("MERGE_MODE"));
 328        unlink(git_path("SQUASH_MSG"));
 329}