Code

Merge branch 'jc/advise-i18n' into maint-1.7.8
[git.git] / builtin / branch.c
index a28a13986d11ebfecd11a206d1b7a1fb626865db..df908ed8f55838702299c9a687a98502f91cf5da 100644 (file)
@@ -19,7 +19,7 @@
 static const char * const builtin_branch_usage[] = {
        "git branch [options] [-r | -a] [--merged | --no-merged]",
        "git branch [options] [-l] [-f] <branchname> [<start-point>]",
-       "git branch [options] [-r] (-d | -D) <branchname>",
+       "git branch [options] [-r] (-d | -D) <branchname>...",
        "git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
        NULL
 };
@@ -43,13 +43,13 @@ enum color_branch {
        BRANCH_COLOR_PLAIN = 1,
        BRANCH_COLOR_REMOTE = 2,
        BRANCH_COLOR_LOCAL = 3,
-       BRANCH_COLOR_CURRENT = 4,
+       BRANCH_COLOR_CURRENT = 4
 };
 
 static enum merge_filter {
        NO_FILTER = 0,
        SHOW_NOT_MERGED,
-       SHOW_MERGED,
+       SHOW_MERGED
 } merge_filter;
 static unsigned char merge_filter_ref[20];
 
@@ -71,7 +71,7 @@ static int parse_branch_color_slot(const char *var, int ofs)
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "color.branch")) {
-               branch_use_color = git_config_colorbool(var, value, -1);
+               branch_use_color = git_config_colorbool(var, value);
                return 0;
        }
        if (!prefixcmp(var, "color.branch.")) {
@@ -88,7 +88,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
 
 static const char *branch_get_color(enum color_branch ix)
 {
-       if (branch_use_color > 0)
+       if (want_color(branch_use_color))
                return branch_colors[ix];
        return "";
 }
@@ -133,12 +133,12 @@ static int branch_merged(int kind, const char *name,
        if ((head_rev != reference_rev) &&
            in_merge_bases(rev, &head_rev, 1) != merged) {
                if (merged)
-                       warning("deleting branch '%s' that has been merged to\n"
-                               "         '%s', but it is not yet merged to HEAD.",
+                       warning(_("deleting branch '%s' that has been merged to\n"
+                               "         '%s', but not yet merged to HEAD."),
                                name, reference_name);
                else
-                       warning("not deleting branch '%s' that is not yet merged to\n"
-                               "         '%s', even though it is merged to HEAD.",
+                       warning(_("not deleting branch '%s' that is not yet merged to\n"
+                               "         '%s', even though it is merged to HEAD."),
                                name, reference_name);
        }
        return merged;
@@ -157,7 +157,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
        switch (kinds) {
        case REF_REMOTE_BRANCH:
                fmt = "refs/remotes/%s";
-               remote = "remote ";
+               /* TRANSLATORS: This is "remote " in "remote branch '%s' not found" */
+               remote = _("remote ");
                force = 1;
                break;
        case REF_LOCAL_BRANCH:
@@ -165,19 +166,19 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                remote = "";
                break;
        default:
-               die("cannot use -a with -d");
+               die(_("cannot use -a with -d"));
        }
 
        if (!force) {
                head_rev = lookup_commit_reference(head_sha1);
                if (!head_rev)
-                       die("Couldn't look up commit object for HEAD");
+                       die(_("Couldn't look up commit object for HEAD"));
        }
        for (i = 0; i < argc; i++, strbuf_release(&bname)) {
                strbuf_branchname(&bname, argv[i]);
                if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
-                       error("Cannot delete the branch '%s' "
-                             "which you are currently on.", bname.buf);
+                       error(_("Cannot delete the branch '%s' "
+                             "which you are currently on."), bname.buf);
                        ret = 1;
                        continue;
                }
@@ -186,7 +187,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 
                name = xstrdup(mkpath(fmt, bname.buf));
                if (!resolve_ref(name, sha1, 1, NULL)) {
-                       error("%sbranch '%s' not found.",
+                       error(_("%sbranch '%s' not found."),
                                        remote, bname.buf);
                        ret = 1;
                        continue;
@@ -194,31 +195,31 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 
                rev = lookup_commit_reference(sha1);
                if (!rev) {
-                       error("Couldn't look up commit object for '%s'", name);
+                       error(_("Couldn't look up commit object for '%s'"), name);
                        ret = 1;
                        continue;
                }
 
                if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) {
-                       error("The branch '%s' is not fully merged.\n"
+                       error(_("The branch '%s' is not fully merged.\n"
                              "If you are sure you want to delete it, "
-                             "run 'git branch -D %s'.", bname.buf, bname.buf);
+                             "run 'git branch -D %s'."), bname.buf, bname.buf);
                        ret = 1;
                        continue;
                }
 
                if (delete_ref(name, sha1, 0)) {
-                       error("Error deleting %sbranch '%s'", remote,
+                       error(_("Error deleting %sbranch '%s'"), remote,
                              bname.buf);
                        ret = 1;
                } else {
                        struct strbuf buf = STRBUF_INIT;
-                       printf("Deleted %sbranch %s (was %s).\n", remote,
+                       printf(_("Deleted %sbranch %s (was %s).\n"), remote,
                               bname.buf,
                               find_unique_abbrev(sha1, DEFAULT_ABBREV));
                        strbuf_addf(&buf, "branch.%s", bname.buf);
                        if (git_config_rename_section(buf.buf, NULL) < 0)
-                               warning("Update of config-file failed");
+                               warning(_("Update of config-file failed"));
                        strbuf_release(&buf);
                }
        }
@@ -257,9 +258,28 @@ static char *resolve_symref(const char *src, const char *prefix)
        return xstrdup(dst);
 }
 
+struct append_ref_cb {
+       struct ref_list *ref_list;
+       const char **pattern;
+       int ret;
+};
+
+static int match_patterns(const char **pattern, const char *refname)
+{
+       if (!*pattern)
+               return 1; /* no pattern always matches */
+       while (*pattern) {
+               if (!fnmatch(*pattern, refname, 0))
+                       return 1;
+               pattern++;
+       }
+       return 0;
+}
+
 static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
-       struct ref_list *ref_list = (struct ref_list*)(cb_data);
+       struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
+       struct ref_list *ref_list = cb->ref_list;
        struct ref_item *newitem;
        struct commit *commit;
        int kind, i;
@@ -290,11 +310,16 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
+       if (!match_patterns(cb->pattern, refname))
+               return 0;
+
        commit = NULL;
        if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
                commit = lookup_commit_reference_gently(sha1, 1);
-               if (!commit)
-                       return error("branch '%s' does not point at a commit", refname);
+               if (!commit) {
+                       cb->ret = error(_("branch '%s' does not point at a commit"), refname);
+                       return 0;
+               }
 
                /* Filter with with_commit if specified */
                if (!is_descendant_of(commit, ref_list->with_commit))
@@ -305,12 +330,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
                                           (struct object *)commit, refname);
        }
 
-       /* Resize buffer */
-       if (ref_list->index >= ref_list->alloc) {
-               ref_list->alloc = alloc_nr(ref_list->alloc);
-               ref_list->list = xrealloc(ref_list->list,
-                               ref_list->alloc * sizeof(struct ref_item));
-       }
+       ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc);
 
        /* Record the new item */
        newitem = &(ref_list->list[ref_list->index++]);
@@ -369,11 +389,11 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
                strbuf_addf(stat, "%s: ",
                        shorten_unambiguous_ref(branch->merge[0]->dst, 0));
        if (!ours)
-               strbuf_addf(stat, "behind %d] ", theirs);
+               strbuf_addf(stat, _("behind %d] "), theirs);
        else if (!theirs)
-               strbuf_addf(stat, "ahead %d] ", ours);
+               strbuf_addf(stat, _("ahead %d] "), ours);
        else
-               strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
+               strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs);
 }
 
 static int matches_merge_filter(struct commit *commit)
@@ -387,6 +407,28 @@ static int matches_merge_filter(struct commit *commit)
        return (is_merged == (merge_filter == SHOW_MERGED));
 }
 
+static void add_verbose_info(struct strbuf *out, struct ref_item *item,
+                            int verbose, int abbrev)
+{
+       struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
+       const char *sub = " **** invalid ref ****";
+       struct commit *commit = item->commit;
+
+       if (commit && !parse_commit(commit)) {
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
+               sub = subject.buf;
+       }
+
+       if (item->kind == REF_LOCAL_BRANCH)
+               fill_tracking_info(&stat, item->name, verbose > 1);
+
+       strbuf_addf(out, " %s %s%s",
+               find_unique_abbrev(item->commit->object.sha1, abbrev),
+               stat.buf, sub);
+       strbuf_release(&stat);
+       strbuf_release(&subject);
+}
+
 static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                           int abbrev, int current, char *prefix)
 {
@@ -427,27 +469,9 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
 
        if (item->dest)
                strbuf_addf(&out, " -> %s", item->dest);
-       else if (verbose) {
-               struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
-               const char *sub = " **** invalid ref ****";
-
-               commit = item->commit;
-               if (commit && !parse_commit(commit)) {
-                       struct pretty_print_context ctx = {0};
-                       pretty_print_commit(CMIT_FMT_ONELINE, commit,
-                                           &subject, &ctx);
-                       sub = subject.buf;
-               }
-
-               if (item->kind == REF_LOCAL_BRANCH)
-                       fill_tracking_info(&stat, item->name, verbose > 1);
-
-               strbuf_addf(&out, " %s %s%s",
-                       find_unique_abbrev(item->commit->object.sha1, abbrev),
-                       stat.buf, sub);
-               strbuf_release(&stat);
-               strbuf_release(&subject);
-       }
+       else if (verbose)
+               /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
+               add_verbose_info(&out, item, verbose, abbrev);
        printf("%s\n", out.buf);
        strbuf_release(&name);
        strbuf_release(&out);
@@ -472,7 +496,7 @@ static void show_detached(struct ref_list *ref_list)
 
        if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
                struct ref_item item;
-               item.name = xstrdup("(no branch)");
+               item.name = xstrdup(_("(no branch)"));
                item.len = strlen(item.name);
                item.kind = REF_LOCAL_BRANCH;
                item.dest = NULL;
@@ -484,9 +508,10 @@ static void show_detached(struct ref_list *ref_list)
        }
 }
 
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
 {
        int i;
+       struct append_ref_cb cb;
        struct ref_list ref_list;
 
        memset(&ref_list, 0, sizeof(ref_list));
@@ -496,7 +521,10 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        ref_list.with_commit = with_commit;
        if (merge_filter != NO_FILTER)
                init_revisions(&ref_list.revs, NULL);
-       for_each_rawref(append_ref, &ref_list);
+       cb.ref_list = &ref_list;
+       cb.pattern = pattern;
+       cb.ret = 0;
+       for_each_rawref(append_ref, &cb);
        if (merge_filter != NO_FILTER) {
                struct commit *filter;
                filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -512,7 +540,7 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
        detached = (detached && (kinds & REF_LOCAL_BRANCH));
-       if (detached)
+       if (detached && match_patterns(pattern, "HEAD"))
                show_detached(&ref_list);
 
        for (i = 0; i < ref_list.index; i++) {
@@ -527,6 +555,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        }
 
        free_ref_list(&ref_list);
+
+       if (cb.ret)
+               error(_("some refs could not be read"));
+
+       return cb.ret;
 }
 
 static void rename_branch(const char *oldname, const char *newname, int force)
@@ -535,9 +568,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        unsigned char sha1[20];
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
        int recovery = 0;
+       int clobber_head_ok;
 
        if (!oldname)
-               die("cannot rename the current branch while not on any.");
+               die(_("cannot rename the current branch while not on any."));
 
        if (strbuf_check_branch_ref(&oldref, oldname)) {
                /*
@@ -547,35 +581,37 @@ static void rename_branch(const char *oldname, const char *newname, int force)
                if (resolve_ref(oldref.buf, sha1, 1, NULL))
                        recovery = 1;
                else
-                       die("Invalid branch name: '%s'", oldname);
+                       die(_("Invalid branch name: '%s'"), oldname);
        }
 
-       if (strbuf_check_branch_ref(&newref, newname))
-               die("Invalid branch name: '%s'", newname);
+       /*
+        * A command like "git branch -M currentbranch currentbranch" cannot
+        * cause the worktree to become inconsistent with HEAD, so allow it.
+        */
+       clobber_head_ok = !strcmp(oldname, newname);
 
-       if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
-               die("A branch named '%s' already exists.", newref.buf + 11);
+       validate_new_branchname(newname, &newref, force, clobber_head_ok);
 
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
 
        if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
-               die("Branch rename failed");
+               die(_("Branch rename failed"));
        strbuf_release(&logmsg);
 
        if (recovery)
-               warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
+               warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
 
        /* no need to pass logmsg here as HEAD didn't really move */
        if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
-               die("Branch renamed to %s, but HEAD is not updated!", newname);
+               die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
 
        strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
        strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
        strbuf_release(&newref);
        if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
-               die("Branch is renamed, but update of config-file failed");
+               die(_("Branch is renamed, but update of config-file failed"));
        strbuf_release(&oldsection);
        strbuf_release(&newsection);
 }
@@ -590,14 +626,14 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
        if (!arg)
                arg = "HEAD";
        if (get_sha1(arg, merge_filter_ref))
-               die("malformed object name %s", arg);
+               die(_("malformed object name %s"), arg);
        return 0;
 }
 
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
-       int delete = 0, rename = 0, force_create = 0;
-       int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
+       int delete = 0, rename = 0, force_create = 0, list = 0;
+       int verbose = 0, abbrev = -1, detached = 0;
        int reflog = 0;
        enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
@@ -605,13 +641,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        struct option options[] = {
                OPT_GROUP("Generic options"),
-               OPT__VERBOSE(&verbose),
+               OPT__VERBOSE(&verbose,
+                       "show hash and subject, give twice for upstream branch"),
                OPT_SET_INT('t', "track",  &track, "set up tracking mode (see git-pull(1))",
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
                        BRANCH_TRACK_OVERRIDE),
-               OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
-               OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
+               OPT__COLOR(&branch_use_color, "use colored output"),
+               OPT_SET_INT('r', "remotes",     &kinds, "act on remote-tracking branches",
                        REF_REMOTE_BRANCH),
                {
                        OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
@@ -628,14 +665,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT__ABBREV(&abbrev),
 
                OPT_GROUP("Specific git-branch actions:"),
-               OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
+               OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
                        REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
-               OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1),
+               OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1),
                OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
-               OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
+               OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
-               OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
-               OPT_BOOLEAN('f', "force", &force_create, "force creation (when already exists)"),
+               OPT_BOOLEAN(0, "list", &list, "list branch names"),
+               OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
+               OPT__FORCE(&force_create, "force creation (when already exists)"),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
                        "commit", "print only not merged branches",
@@ -651,44 +689,55 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
-       git_config(git_branch_config, NULL);
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(builtin_branch_usage, options);
 
-       if (branch_use_color == -1)
-               branch_use_color = git_use_color_default;
+       git_config(git_branch_config, NULL);
 
        track = git_branch_track;
 
        head = resolve_ref("HEAD", head_sha1, 0, NULL);
        if (!head)
-               die("Failed to resolve HEAD as a valid ref.");
+               die(_("Failed to resolve HEAD as a valid ref."));
        head = xstrdup(head);
        if (!strcmp(head, "HEAD")) {
                detached = 1;
        } else {
                if (prefixcmp(head, "refs/heads/"))
-                       die("HEAD not found below refs/heads!");
+                       die(_("HEAD not found below refs/heads!"));
                head += 11;
        }
        hashcpy(merge_filter_ref, head_sha1);
 
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
-       if (!!delete + !!rename + !!force_create > 1)
+
+       if (!delete && !rename && argc == 0)
+               list = 1;
+
+       if (!!delete + !!rename + !!force_create + !!list > 1)
                usage_with_options(builtin_branch_usage, options);
 
+       if (abbrev == -1)
+               abbrev = DEFAULT_ABBREV;
+
        if (delete)
                return delete_branches(argc, argv, delete > 1, kinds);
-       else if (argc == 0)
-               print_ref_list(kinds, detached, verbose, abbrev, with_commit);
-       else if (rename && (argc == 1))
-               rename_branch(head, argv[0], rename > 1);
-       else if (rename && (argc == 2))
-               rename_branch(argv[0], argv[1], rename > 1);
-       else if (argc <= 2) {
+       else if (list)
+               return print_ref_list(kinds, detached, verbose, abbrev,
+                                     with_commit, argv);
+       else if (rename) {
+               if (argc == 1)
+                       rename_branch(head, argv[0], rename > 1);
+               else if (argc == 2)
+                       rename_branch(argv[0], argv[1], rename > 1);
+               else
+                       usage_with_options(builtin_branch_usage, options);
+       } else if (argc > 0 && argc <= 2) {
                if (kinds != REF_LOCAL_BRANCH)
-                       die("-a and -r options to 'git branch' do not make sense with a branch name");
+                       die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
-                             force_create, reflog, track);
+                             force_create, reflog, 0, track);
        } else
                usage_with_options(builtin_branch_usage, options);