Code

Merge branch 'jn/branch-move-to-self'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Dec 2011 06:53:08 +0000 (22:53 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Dec 2011 06:53:08 +0000 (22:53 -0800)
* jn/branch-move-to-self:
  Allow checkout -B <current-branch> to update the current branch
  branch: allow a no-op "branch -M <current-branch> HEAD"

1  2 
branch.c
branch.h
builtin/branch.c
builtin/checkout.c

diff --combined branch.c
index d91a099fdd22b9131a1d2ddaf3778b645c53eca0,f85c438284d6b810617595971d7e3ec2e79e3120..a715a1174982970943ed2c4fc1c2ab4906699fdc
+++ b/branch.c
@@@ -136,37 -136,6 +136,37 @@@ static int setup_tracking(const char *n
        return 0;
  }
  
 +struct branch_desc_cb {
 +      const char *config_name;
 +      const char *value;
 +};
 +
 +static int read_branch_desc_cb(const char *var, const char *value, void *cb)
 +{
 +      struct branch_desc_cb *desc = cb;
 +      if (strcmp(desc->config_name, var))
 +              return 0;
 +      free((char *)desc->value);
 +      return git_config_string(&desc->value, var, value);
 +}
 +
 +int read_branch_desc(struct strbuf *buf, const char *branch_name)
 +{
 +      struct branch_desc_cb cb;
 +      struct strbuf name = STRBUF_INIT;
 +      strbuf_addf(&name, "branch.%s.description", branch_name);
 +      cb.config_name = name.buf;
 +      cb.value = NULL;
 +      if (git_config(read_branch_desc_cb, &cb) < 0) {
 +              strbuf_release(&name);
 +              return -1;
 +      }
 +      if (cb.value)
 +              strbuf_addstr(buf, cb.value);
 +      strbuf_release(&name);
 +      return 0;
 +}
 +
  int validate_new_branchname(const char *name, struct strbuf *ref,
                            int force, int attr_only)
  {
  
  void create_branch(const char *head,
                   const char *name, const char *start_name,
-                  int force, int reflog, enum branch_track track)
+                  int force, int reflog, int clobber_head,
+                  enum branch_track track)
  {
        struct ref_lock *lock = NULL;
        struct commit *commit;
                explicit_tracking = 1;
  
        if (validate_new_branchname(name, &ref, force,
-                                   track == BRANCH_TRACK_OVERRIDE)) {
+                                   track == BRANCH_TRACK_OVERRIDE ||
+                                   clobber_head)) {
                if (!force)
                        dont_change_ref = 1;
                else
diff --combined branch.h
index 1493f73722161cc191f0c7fd655781d8b748ed41,e125ff4ca89a87fadb03b3d8272b44ab33640c72..b99c5a369e31a85d1fff822460e69a79d8c6102b
+++ b/branch.h
@@@ -13,7 -13,8 +13,8 @@@
   * branch for (if any).
   */
  void create_branch(const char *head, const char *name, const char *start_name,
-                  int force, int reflog, enum branch_track track);
+                  int force, int reflog,
+                  int clobber_head, enum branch_track track);
  
  /*
   * Validates that the requested branch may be created, returning the
@@@ -46,9 -47,4 +47,9 @@@ void remove_branch_state(void)
  #define BRANCH_CONFIG_VERBOSE 01
  extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
  
 +/*
 + * Read branch description
 + */
 +extern int read_branch_desc(struct strbuf *, const char *branch_name);
 +
  #endif
diff --combined builtin/branch.c
index e1e486e4c51194e09df3779be254cb72855d1103,823789fc95f609ca5a763be545f3963c2fa5a1ec..465ff6a513f3e314d4a526b6f8ee9f7a7c7b43f4
@@@ -115,10 -115,8 +115,10 @@@ static int branch_merged(int kind, cons
                    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;
                                "         '%s', even though it is merged to HEAD."),
                                name, reference_name);
        }
 +      free((char *)reference_name);
        return merged;
  }
  
@@@ -189,7 -186,7 +189,7 @@@ static int delete_branches(int argc, co
                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;
@@@ -568,8 -565,10 +568,9 @@@ static int print_ref_list(int kinds, in
  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;
+       int clobber_head_ok;
  
        if (!oldname)
                die(_("cannot rename the current branch while not on any."));
                 * 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);
        }
  
-       validate_new_branchname(newname, &newref, force, 0);
+       /*
+        * 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);
+       validate_new_branchname(newname, &newref, force, clobber_head_ok);
  
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
@@@ -625,49 -630,11 +632,49 @@@ static int opt_parse_merge_filter(cons
        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, list = 0;
        int verbose = 0, abbrev = -1, detached = 0;
 -      int reflog = 0;
 +      int reflog = 0, edit_description = 0;
        enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
        struct commit_list *with_commit = NULL;
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
                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,
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
  
 -      if (!delete && !rename && !force_create && argc == 0)
 +      if (!delete && !rename && !edit_description && argc == 0)
                list = 1;
  
        if (!!delete + !!rename + !!force_create + !!list > 1)
        else if (list)
                return print_ref_list(kinds, detached, verbose, abbrev,
                                      with_commit, argv);
 -      else if (rename) {
 +      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 <= 2) {
 +      } 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,
-                             force_create, reflog, track);
+                             force_create, reflog, 0, track);
        } else
                usage_with_options(builtin_branch_usage, options);
  
diff --combined builtin/checkout.c
index b7c630287dda662630af9883655ed23bd50e7b7e,ca00a8538ae2e49458046a41aa48f3285a9b6de9..3f5d9b629a515d1531ff801fabdb9a72246d5d5c
@@@ -288,7 -288,7 +288,7 @@@ static int checkout_paths(struct tree *
            commit_locked_index(lock_file))
                die(_("unable to write new index file"));
  
 -      resolve_ref("HEAD", rev, 0, &flag);
 +      read_ref_full("HEAD", rev, 0, &flag);
        head = lookup_commit_reference_gently(rev, 1);
  
        errs |= post_checkout_hook(head, head, 0);
@@@ -411,7 -411,7 +411,7 @@@ static int merge_working_tree(struct ch
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
 -              topts.dir->exclude_per_dir = ".gitignore";
 +              setup_standard_excludes(topts.dir);
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           EMPTY_TREE_SHA1_BIN);
@@@ -540,7 -540,9 +540,9 @@@ static void update_refs_for_switch(stru
                else
                        create_branch(old->name, opts->new_branch, new->name,
                                      opts->new_branch_force ? 1 : 0,
-                                     opts->new_branch_log, opts->track);
+                                     opts->new_branch_log,
+                                     opts->new_branch_force ? 1 : 0,
+                                     opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
        }
                create_symref("HEAD", new->path, msg.buf);
                if (!opts->quiet) {
                        if (old->path && !strcmp(new->path, old->path)) {
-                               fprintf(stderr, _("Already on '%s'\n"),
-                                       new->name);
+                               if (opts->new_branch_force)
+                                       fprintf(stderr, _("Reset branch '%s'\n"),
+                                               new->name);
+                               else
+                                       fprintf(stderr, _("Already on '%s'\n"),
+                                               new->name);
                        } else if (opts->new_branch) {
                                if (opts->branch_exists)
                                        fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
@@@ -699,9 -705,7 +705,9 @@@ static int switch_branches(struct check
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
 -      old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
 +      old.path = resolve_ref("HEAD", rev, 0, &flag);
 +      if (old.path)
 +              old.path = xstrdup(old.path);
        old.commit = lookup_commit_reference_gently(rev, 1);
        if (!(flag & REF_ISSYMREF)) {
                free((char *)old.path);
@@@ -868,7 -872,7 +874,7 @@@ static int parse_branchname_arg(int arg
        setup_branch_path(new);
  
        if (!check_refname_format(new->path, 0) &&
 -          resolve_ref(new->path, branch_rev, 1, NULL))
 +          !read_ref(new->path, branch_rev))
                hashcpy(rev, branch_rev);
        else
                new->path = NULL; /* not an existing branch */
@@@ -1059,7 -1063,8 +1065,8 @@@ int cmd_checkout(int argc, const char *
                struct strbuf buf = STRBUF_INIT;
  
                opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
-                                                            !!opts.new_branch_force, 0);
+                                                            !!opts.new_branch_force,
+                                                            !!opts.new_branch_force);
  
                strbuf_release(&buf);
        }