Merge branch 'tj/maint-imap-send-remove-unused'
[gitweb.git] / builtin / branch.c
index 3142daa57a6fa1c8a7d21095946bf5d26443d0e0..e1e486e4c51194e09df3779be254cb72855d1103 100644 (file)
@@ -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 "";
 }
@@ -115,8 +115,10 @@ static int branch_merged(int kind, const char *name,
                    branch->merge[0] &&
                    branch->merge[0]->dst &&
                    (reference_name =
-                    resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+                    resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL) {
+                       reference_name = xstrdup(reference_name);
                        reference_rev = lookup_commit_reference(sha1);
+               }
        }
        if (!reference_rev)
                reference_rev = head_rev;
@@ -141,6 +143,7 @@ static int branch_merged(int kind, const char *name,
                                "         '%s', even though it is merged to HEAD."),
                                name, reference_name);
        }
+       free((char *)reference_name);
        return merged;
 }
 
@@ -186,7 +189,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                free(name);
 
                name = xstrdup(mkpath(fmt, bname.buf));
-               if (!resolve_ref(name, sha1, 1, NULL)) {
+               if (read_ref(name, sha1)) {
                        error(_("%sbranch '%s' not found."),
                                        remote, bname.buf);
                        ret = 1;
@@ -260,9 +263,22 @@ static char *resolve_symref(const char *src, const char *prefix)
 
 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 append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
@@ -297,6 +313,9 @@ 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);
@@ -492,7 +511,7 @@ static void show_detached(struct ref_list *ref_list)
        }
 }
 
-static int 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;
@@ -506,6 +525,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
        if (merge_filter != NO_FILTER)
                init_revisions(&ref_list.revs, NULL);
        cb.ref_list = &ref_list;
+       cb.pattern = pattern;
        cb.ret = 0;
        for_each_rawref(append_ref, &cb);
        if (merge_filter != NO_FILTER) {
@@ -523,7 +543,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
        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++) {
@@ -548,7 +568,6 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
-       unsigned char sha1[20];
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
        int recovery = 0;
 
@@ -560,17 +579,13 @@ static void rename_branch(const char *oldname, const char *newname, int force)
                 * Bad name --- this could be an attempt to rename a
                 * ref that we used to allow to be created by accident.
                 */
-               if (resolve_ref(oldref.buf, sha1, 1, NULL))
+               if (ref_exists(oldref.buf))
                        recovery = 1;
                else
                        die(_("Invalid branch name: '%s'"), oldname);
        }
 
-       if (strbuf_check_branch_ref(&newref, newname))
-               die(_("Invalid branch name: '%s'"), 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, 0);
 
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
@@ -610,11 +625,49 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
        return 0;
 }
 
+static const char edit_description[] = "BRANCH_DESCRIPTION";
+
+static int edit_branch_description(const char *branch_name)
+{
+       FILE *fp;
+       int status;
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf name = STRBUF_INIT;
+
+       read_branch_desc(&buf, branch_name);
+       if (!buf.len || buf.buf[buf.len-1] != '\n')
+               strbuf_addch(&buf, '\n');
+       strbuf_addf(&buf,
+                   "# Please edit the description for the branch\n"
+                   "#   %s\n"
+                   "# Lines starting with '#' will be stripped.\n",
+                   branch_name);
+       fp = fopen(git_path(edit_description), "w");
+       if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+               strbuf_release(&buf);
+               return error(_("could not write branch description template: %s\n"),
+                            strerror(errno));
+       }
+       strbuf_reset(&buf);
+       if (launch_editor(git_path(edit_description), &buf, NULL)) {
+               strbuf_release(&buf);
+               return -1;
+       }
+       stripspace(&buf, 1);
+
+       strbuf_addf(&name, "branch.%s.description", branch_name);
+       status = git_config_set(name.buf, buf.buf);
+       strbuf_release(&name);
+       strbuf_release(&buf);
+
+       return status;
+}
+
 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 reflog = 0;
+       int delete = 0, rename = 0, force_create = 0, list = 0;
+       int verbose = 0, abbrev = -1, detached = 0;
+       int reflog = 0, edit_description = 0;
        enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
        struct commit_list *with_commit = NULL;
@@ -628,7 +681,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
                        BRANCH_TRACK_OVERRIDE),
                OPT__COLOR(&branch_use_color, "use colored output"),
-               OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
+               OPT_SET_INT('r', "remotes",     &kinds, "act on remote-tracking branches",
                        REF_REMOTE_BRANCH),
                {
                        OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
@@ -645,13 +698,16 @@ 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(0, "list", &list, "list branch names"),
+               OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
+               OPT_BOOLEAN(0, "edit-description", &edit_description,
+                           "edit the description for the branch"),
                OPT__FORCE(&force_create, "force creation (when already exists)"),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
@@ -673,9 +729,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        git_config(git_branch_config, NULL);
 
-       if (branch_use_color == -1)
-               branch_use_color = git_use_color_default;
-
        track = git_branch_track;
 
        head = resolve_ref("HEAD", head_sha1, 0, NULL);
@@ -693,18 +746,41 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
-       if (!!delete + !!rename + !!force_create > 1)
+
+       if (!delete && !rename && !edit_description && 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)
-               return 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 (edit_description) {
+               const char *branch_name;
+               if (detached)
+                       die("Cannot give description to detached HEAD");
+               if (!argc)
+                       branch_name = head;
+               else if (argc == 1)
+                       branch_name = argv[0];
+               else
+                       usage_with_options(builtin_branch_usage, options);
+               if (edit_branch_description(branch_name))
+                       return 1;
+       } 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"));
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,