builtin-branch.con commit Merge branch 'jc/format-patch-encoding' (140dd77)
   1/*
   2 * Builtin "git branch"
   3 *
   4 * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
   5 * Based on git-branch.sh by Junio C Hamano.
   6 */
   7
   8#include "cache.h"
   9#include "color.h"
  10#include "refs.h"
  11#include "commit.h"
  12#include "builtin.h"
  13#include "remote.h"
  14#include "parse-options.h"
  15
  16static const char * const builtin_branch_usage[] = {
  17        "git-branch [options] [-r | -a]",
  18        "git-branch [options] [-l] [-f] <branchname> [<start-point>]",
  19        "git-branch [options] [-r] (-d | -D) <branchname>",
  20        "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>",
  21        NULL
  22};
  23
  24#define REF_UNKNOWN_TYPE    0x00
  25#define REF_LOCAL_BRANCH    0x01
  26#define REF_REMOTE_BRANCH   0x02
  27#define REF_TAG             0x04
  28
  29static const char *head;
  30static unsigned char head_sha1[20];
  31
  32static int branch_track = 1;
  33
  34static int branch_use_color;
  35static char branch_colors[][COLOR_MAXLEN] = {
  36        "\033[m",       /* reset */
  37        "",             /* PLAIN (normal) */
  38        "\033[31m",     /* REMOTE (red) */
  39        "",             /* LOCAL (normal) */
  40        "\033[32m",     /* CURRENT (green) */
  41};
  42enum color_branch {
  43        COLOR_BRANCH_RESET = 0,
  44        COLOR_BRANCH_PLAIN = 1,
  45        COLOR_BRANCH_REMOTE = 2,
  46        COLOR_BRANCH_LOCAL = 3,
  47        COLOR_BRANCH_CURRENT = 4,
  48};
  49
  50static int parse_branch_color_slot(const char *var, int ofs)
  51{
  52        if (!strcasecmp(var+ofs, "plain"))
  53                return COLOR_BRANCH_PLAIN;
  54        if (!strcasecmp(var+ofs, "reset"))
  55                return COLOR_BRANCH_RESET;
  56        if (!strcasecmp(var+ofs, "remote"))
  57                return COLOR_BRANCH_REMOTE;
  58        if (!strcasecmp(var+ofs, "local"))
  59                return COLOR_BRANCH_LOCAL;
  60        if (!strcasecmp(var+ofs, "current"))
  61                return COLOR_BRANCH_CURRENT;
  62        die("bad config variable '%s'", var);
  63}
  64
  65static int git_branch_config(const char *var, const char *value)
  66{
  67        if (!strcmp(var, "color.branch")) {
  68                branch_use_color = git_config_colorbool(var, value);
  69                return 0;
  70        }
  71        if (!prefixcmp(var, "color.branch.")) {
  72                int slot = parse_branch_color_slot(var, 13);
  73                color_parse(value, var, branch_colors[slot]);
  74                return 0;
  75        }
  76        if (!strcmp(var, "branch.autosetupmerge"))
  77                        branch_track = git_config_bool(var, value);
  78
  79        return git_default_config(var, value);
  80}
  81
  82static const char *branch_get_color(enum color_branch ix)
  83{
  84        if (branch_use_color)
  85                return branch_colors[ix];
  86        return "";
  87}
  88
  89static int delete_branches(int argc, const char **argv, int force, int kinds)
  90{
  91        struct commit *rev, *head_rev = head_rev;
  92        unsigned char sha1[20];
  93        char *name = NULL;
  94        const char *fmt, *remote;
  95        char section[PATH_MAX];
  96        int i;
  97        int ret = 0;
  98
  99        switch (kinds) {
 100        case REF_REMOTE_BRANCH:
 101                fmt = "refs/remotes/%s";
 102                remote = "remote ";
 103                force = 1;
 104                break;
 105        case REF_LOCAL_BRANCH:
 106                fmt = "refs/heads/%s";
 107                remote = "";
 108                break;
 109        default:
 110                die("cannot use -a with -d");
 111        }
 112
 113        if (!force) {
 114                head_rev = lookup_commit_reference(head_sha1);
 115                if (!head_rev)
 116                        die("Couldn't look up commit object for HEAD");
 117        }
 118        for (i = 0; i < argc; i++) {
 119                if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) {
 120                        error("Cannot delete the branch '%s' "
 121                                "which you are currently on.", argv[i]);
 122                        ret = 1;
 123                        continue;
 124                }
 125
 126                if (name)
 127                        free(name);
 128
 129                name = xstrdup(mkpath(fmt, argv[i]));
 130                if (!resolve_ref(name, sha1, 1, NULL)) {
 131                        error("%sbranch '%s' not found.",
 132                                        remote, argv[i]);
 133                        ret = 1;
 134                        continue;
 135                }
 136
 137                rev = lookup_commit_reference(sha1);
 138                if (!rev) {
 139                        error("Couldn't look up commit object for '%s'", name);
 140                        ret = 1;
 141                        continue;
 142                }
 143
 144                /* This checks whether the merge bases of branch and
 145                 * HEAD contains branch -- which means that the HEAD
 146                 * contains everything in both.
 147                 */
 148
 149                if (!force &&
 150                    !in_merge_bases(rev, &head_rev, 1)) {
 151                        error("The branch '%s' is not an ancestor of "
 152                                "your current HEAD.\n"
 153                                "If you are sure you want to delete it, "
 154                                "run 'git branch -D %s'.", argv[i], argv[i]);
 155                        ret = 1;
 156                        continue;
 157                }
 158
 159                if (delete_ref(name, sha1)) {
 160                        error("Error deleting %sbranch '%s'", remote,
 161                               argv[i]);
 162                        ret = 1;
 163                } else {
 164                        printf("Deleted %sbranch %s.\n", remote, argv[i]);
 165                        snprintf(section, sizeof(section), "branch.%s",
 166                                 argv[i]);
 167                        if (git_config_rename_section(section, NULL) < 0)
 168                                warning("Update of config-file failed");
 169                }
 170        }
 171
 172        if (name)
 173                free(name);
 174
 175        return(ret);
 176}
 177
 178struct ref_item {
 179        char *name;
 180        unsigned int kind;
 181        unsigned char sha1[20];
 182};
 183
 184struct ref_list {
 185        int index, alloc, maxwidth;
 186        struct ref_item *list;
 187        int kinds;
 188};
 189
 190static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 191{
 192        struct ref_list *ref_list = (struct ref_list*)(cb_data);
 193        struct ref_item *newitem;
 194        int kind = REF_UNKNOWN_TYPE;
 195        int len;
 196
 197        /* Detect kind */
 198        if (!prefixcmp(refname, "refs/heads/")) {
 199                kind = REF_LOCAL_BRANCH;
 200                refname += 11;
 201        } else if (!prefixcmp(refname, "refs/remotes/")) {
 202                kind = REF_REMOTE_BRANCH;
 203                refname += 13;
 204        } else if (!prefixcmp(refname, "refs/tags/")) {
 205                kind = REF_TAG;
 206                refname += 10;
 207        }
 208
 209        /* Don't add types the caller doesn't want */
 210        if ((kind & ref_list->kinds) == 0)
 211                return 0;
 212
 213        /* Resize buffer */
 214        if (ref_list->index >= ref_list->alloc) {
 215                ref_list->alloc = alloc_nr(ref_list->alloc);
 216                ref_list->list = xrealloc(ref_list->list,
 217                                ref_list->alloc * sizeof(struct ref_item));
 218        }
 219
 220        /* Record the new item */
 221        newitem = &(ref_list->list[ref_list->index++]);
 222        newitem->name = xstrdup(refname);
 223        newitem->kind = kind;
 224        hashcpy(newitem->sha1, sha1);
 225        len = strlen(newitem->name);
 226        if (len > ref_list->maxwidth)
 227                ref_list->maxwidth = len;
 228
 229        return 0;
 230}
 231
 232static void free_ref_list(struct ref_list *ref_list)
 233{
 234        int i;
 235
 236        for (i = 0; i < ref_list->index; i++)
 237                free(ref_list->list[i].name);
 238        free(ref_list->list);
 239}
 240
 241static int ref_cmp(const void *r1, const void *r2)
 242{
 243        struct ref_item *c1 = (struct ref_item *)(r1);
 244        struct ref_item *c2 = (struct ref_item *)(r2);
 245
 246        if (c1->kind != c2->kind)
 247                return c1->kind - c2->kind;
 248        return strcmp(c1->name, c2->name);
 249}
 250
 251static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
 252                           int abbrev, int current)
 253{
 254        char c;
 255        int color;
 256        struct commit *commit;
 257
 258        switch (item->kind) {
 259        case REF_LOCAL_BRANCH:
 260                color = COLOR_BRANCH_LOCAL;
 261                break;
 262        case REF_REMOTE_BRANCH:
 263                color = COLOR_BRANCH_REMOTE;
 264                break;
 265        default:
 266                color = COLOR_BRANCH_PLAIN;
 267                break;
 268        }
 269
 270        c = ' ';
 271        if (current) {
 272                c = '*';
 273                color = COLOR_BRANCH_CURRENT;
 274        }
 275
 276        if (verbose) {
 277                struct strbuf subject;
 278                const char *sub = " **** invalid ref ****";
 279
 280                strbuf_init(&subject, 0);
 281
 282                commit = lookup_commit(item->sha1);
 283                if (commit && !parse_commit(commit)) {
 284                        pretty_print_commit(CMIT_FMT_ONELINE, commit,
 285                                            &subject, 0, NULL, NULL, 0, 0);
 286                        sub = subject.buf;
 287                }
 288                printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
 289                       maxwidth, item->name,
 290                       branch_get_color(COLOR_BRANCH_RESET),
 291                       find_unique_abbrev(item->sha1, abbrev), sub);
 292                strbuf_release(&subject);
 293        } else {
 294                printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
 295                       branch_get_color(COLOR_BRANCH_RESET));
 296        }
 297}
 298
 299static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
 300{
 301        int i;
 302        struct ref_list ref_list;
 303
 304        memset(&ref_list, 0, sizeof(ref_list));
 305        ref_list.kinds = kinds;
 306        for_each_ref(append_ref, &ref_list);
 307
 308        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 309
 310        detached = (detached && (kinds & REF_LOCAL_BRANCH));
 311        if (detached) {
 312                struct ref_item item;
 313                item.name = xstrdup("(no branch)");
 314                item.kind = REF_LOCAL_BRANCH;
 315                hashcpy(item.sha1, head_sha1);
 316                if (strlen(item.name) > ref_list.maxwidth)
 317                              ref_list.maxwidth = strlen(item.name);
 318                print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
 319                free(item.name);
 320        }
 321
 322        for (i = 0; i < ref_list.index; i++) {
 323                int current = !detached &&
 324                        (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
 325                        !strcmp(ref_list.list[i].name, head);
 326                print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
 327                               abbrev, current);
 328        }
 329
 330        free_ref_list(&ref_list);
 331}
 332
 333struct tracking {
 334        struct refspec spec;
 335        char *src;
 336        const char *remote;
 337        int matches;
 338};
 339
 340static int find_tracked_branch(struct remote *remote, void *priv)
 341{
 342        struct tracking *tracking = priv;
 343
 344        if (!remote_find_tracking(remote, &tracking->spec)) {
 345                if (++tracking->matches == 1) {
 346                        tracking->src = tracking->spec.src;
 347                        tracking->remote = remote->name;
 348                } else {
 349                        free(tracking->spec.src);
 350                        if (tracking->src) {
 351                                free(tracking->src);
 352                                tracking->src = NULL;
 353                        }
 354                }
 355                tracking->spec.src = NULL;
 356        }
 357
 358        return 0;
 359}
 360
 361
 362/*
 363 * This is called when new_ref is branched off of orig_ref, and tries
 364 * to infer the settings for branch.<new_ref>.{remote,merge} from the
 365 * config.
 366 */
 367static int setup_tracking(const char *new_ref, const char *orig_ref)
 368{
 369        char key[1024];
 370        struct tracking tracking;
 371
 372        if (strlen(new_ref) > 1024 - 7 - 7 - 1)
 373                return error("Tracking not set up: name too long: %s",
 374                                new_ref);
 375
 376        memset(&tracking, 0, sizeof(tracking));
 377        tracking.spec.dst = (char *)orig_ref;
 378        if (for_each_remote(find_tracked_branch, &tracking) ||
 379                        !tracking.matches)
 380                return 1;
 381
 382        if (tracking.matches > 1)
 383                return error("Not tracking: ambiguous information for ref %s",
 384                                orig_ref);
 385
 386        if (tracking.matches == 1) {
 387                sprintf(key, "branch.%s.remote", new_ref);
 388                git_config_set(key, tracking.remote ?  tracking.remote : ".");
 389                sprintf(key, "branch.%s.merge", new_ref);
 390                git_config_set(key, tracking.src);
 391                free(tracking.src);
 392                printf("Branch %s set up to track remote branch %s.\n",
 393                               new_ref, orig_ref);
 394        }
 395
 396        return 0;
 397}
 398
 399static void create_branch(const char *name, const char *start_name,
 400                          int force, int reflog, int track)
 401{
 402        struct ref_lock *lock;
 403        struct commit *commit;
 404        unsigned char sha1[20];
 405        char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
 406        int forcing = 0;
 407
 408        snprintf(ref, sizeof ref, "refs/heads/%s", name);
 409        if (check_ref_format(ref))
 410                die("'%s' is not a valid branch name.", name);
 411
 412        if (resolve_ref(ref, sha1, 1, NULL)) {
 413                if (!force)
 414                        die("A branch named '%s' already exists.", name);
 415                else if (!is_bare_repository() && !strcmp(head, name))
 416                        die("Cannot force update the current branch.");
 417                forcing = 1;
 418        }
 419
 420        real_ref = NULL;
 421        if (get_sha1(start_name, sha1))
 422                die("Not a valid object name: '%s'.", start_name);
 423
 424        switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
 425        case 0:
 426                /* Not branching from any existing branch */
 427                real_ref = NULL;
 428                break;
 429        case 1:
 430                /* Unique completion -- good */
 431                break;
 432        default:
 433                die("Ambiguous object name: '%s'.", start_name);
 434                break;
 435        }
 436
 437        if ((commit = lookup_commit_reference(sha1)) == NULL)
 438                die("Not a valid branch point: '%s'.", start_name);
 439        hashcpy(sha1, commit->object.sha1);
 440
 441        lock = lock_any_ref_for_update(ref, NULL, 0);
 442        if (!lock)
 443                die("Failed to lock ref for update: %s.", strerror(errno));
 444
 445        if (reflog)
 446                log_all_ref_updates = 1;
 447
 448        if (forcing)
 449                snprintf(msg, sizeof msg, "branch: Reset from %s",
 450                         start_name);
 451        else
 452                snprintf(msg, sizeof msg, "branch: Created from %s",
 453                         start_name);
 454
 455        /* When branching off a remote branch, set up so that git-pull
 456           automatically merges from there.  So far, this is only done for
 457           remotes registered via .git/config.  */
 458        if (real_ref && track)
 459                setup_tracking(name, real_ref);
 460
 461        if (write_ref_sha1(lock, sha1, msg) < 0)
 462                die("Failed to write ref: %s.", strerror(errno));
 463
 464        if (real_ref)
 465                free(real_ref);
 466}
 467
 468static void rename_branch(const char *oldname, const char *newname, int force)
 469{
 470        char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
 471        unsigned char sha1[20];
 472        char oldsection[PATH_MAX], newsection[PATH_MAX];
 473
 474        if (!oldname)
 475                die("cannot rename the current branch while not on any.");
 476
 477        if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
 478                die("Old branchname too long");
 479
 480        if (check_ref_format(oldref))
 481                die("Invalid branch name: %s", oldref);
 482
 483        if (snprintf(newref, sizeof(newref), "refs/heads/%s", newname) > sizeof(newref))
 484                die("New branchname too long");
 485
 486        if (check_ref_format(newref))
 487                die("Invalid branch name: %s", newref);
 488
 489        if (resolve_ref(newref, sha1, 1, NULL) && !force)
 490                die("A branch named '%s' already exists.", newname);
 491
 492        snprintf(logmsg, sizeof(logmsg), "Branch: renamed %s to %s",
 493                 oldref, newref);
 494
 495        if (rename_ref(oldref, newref, logmsg))
 496                die("Branch rename failed");
 497
 498        /* no need to pass logmsg here as HEAD didn't really move */
 499        if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL))
 500                die("Branch renamed to %s, but HEAD is not updated!", newname);
 501
 502        snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11);
 503        snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11);
 504        if (git_config_rename_section(oldsection, newsection) < 0)
 505                die("Branch is renamed, but update of config-file failed");
 506}
 507
 508int cmd_branch(int argc, const char **argv, const char *prefix)
 509{
 510        int delete = 0, force_delete = 0, force_create = 0;
 511        int rename = 0, force_rename = 0;
 512        int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
 513        int reflog = 0, track;
 514        int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0;
 515
 516        struct option options[] = {
 517                OPT_GROUP("Generic options"),
 518                OPT__VERBOSE(&verbose),
 519                OPT_BOOLEAN( 0 , "track",  &track, "set up tracking mode (see git-pull(1))"),
 520                OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
 521                OPT_BOOLEAN('r', NULL,     &kind_remote, "act on remote-tracking branches"),
 522                OPT__ABBREV(&abbrev),
 523
 524                OPT_GROUP("Specific git-branch actions:"),
 525                OPT_BOOLEAN('a', NULL,     &kind_any, "list both remote-tracking and local branches"),
 526                OPT_BOOLEAN('d', NULL,     &delete, "delete fully merged branch"),
 527                OPT_BOOLEAN('D', NULL,     &force_delete, "delete branch (even if not merged)"),
 528                OPT_BOOLEAN('l', NULL,     &reflog, "create the branch's reflog"),
 529                OPT_BOOLEAN('f', NULL,     &force_create, "force creation (when already exists)"),
 530                OPT_BOOLEAN('m', NULL,     &rename, "move/rename a branch and its reflog"),
 531                OPT_BOOLEAN('M', NULL,     &force_rename, "move/rename a branch, even if target exists"),
 532                OPT_END(),
 533        };
 534
 535        git_config(git_branch_config);
 536        track = branch_track;
 537        argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
 538
 539        delete |= force_delete;
 540        rename |= force_rename;
 541        if (kind_remote)
 542                kinds = REF_REMOTE_BRANCH;
 543        if (kind_any)
 544                kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
 545        if (abbrev && abbrev < MINIMUM_ABBREV)
 546                abbrev = MINIMUM_ABBREV;
 547        else if (abbrev > 40)
 548                abbrev = 40;
 549
 550        if ((delete && rename) || (delete && force_create) ||
 551            (rename && force_create))
 552                usage_with_options(builtin_branch_usage, options);
 553
 554        head = resolve_ref("HEAD", head_sha1, 0, NULL);
 555        if (!head)
 556                die("Failed to resolve HEAD as a valid ref.");
 557        head = xstrdup(head);
 558        if (!strcmp(head, "HEAD")) {
 559                detached = 1;
 560        } else {
 561                if (prefixcmp(head, "refs/heads/"))
 562                        die("HEAD not found below refs/heads!");
 563                head += 11;
 564        }
 565
 566        if (delete)
 567                return delete_branches(argc, argv, force_delete, kinds);
 568        else if (argc == 0)
 569                print_ref_list(kinds, detached, verbose, abbrev);
 570        else if (rename && (argc == 1))
 571                rename_branch(head, argv[0], force_rename);
 572        else if (rename && (argc == 2))
 573                rename_branch(argv[0], argv[1], force_rename);
 574        else if (argc <= 2)
 575                create_branch(argv[0], (argc == 2) ? argv[1] : head,
 576                              force_create, reflog, track);
 577        else
 578                usage_with_options(builtin_branch_usage, options);
 579
 580        return 0;
 581}