Merge branch 'jc/maint-co-track'
authorJunio C Hamano <gitster@pobox.com>
Wed, 22 Oct 2008 00:58:11 +0000 (17:58 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 22 Oct 2008 00:58:11 +0000 (17:58 -0700)
* jc/maint-co-track:
Enhance hold_lock_file_for_{update,append}() API
demonstrate breakage of detached checkout with symbolic link HEAD
Fix "checkout --track -b newbranch" on detached HEAD

Conflicts:
builtin-commit.c

1  2 
branch.c
builtin-commit.c
builtin-fetch-pack.c
builtin-revert.c
cache.h
refs.c
rerere.c
sha1_file.c
t/t7201-co.sh
diff --combined branch.c
index 205b89dc697eb4475355ab5b2a6528e404901c0c,6a750574fd376e4d54e4ef2576674d42521f1529..b1ac837f3d30c826ddbe29492b906bf2d0de0a1a
+++ b/branch.c
@@@ -129,7 -129,9 +129,9 @@@ void create_branch(const char *head
                        die("Cannot setup tracking information; starting point is not a branch.");
                break;
        case 1:
-               /* Unique completion -- good */
+               /* Unique completion -- good, only if it is a real ref */
+               if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD"))
+                       die("Cannot setup tracking information; starting point is not a branch.");
                break;
        default:
                die("Ambiguous object name: '%s'.", start_name);
@@@ -168,6 -170,5 +170,6 @@@ void remove_branch_state(void
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_RR"));
        unlink(git_path("MERGE_MSG"));
 +      unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
  }
diff --combined builtin-commit.c
index 33b659edce478fb0d3d4bb723e4f54c13db0efb1,b563a0d67cedc66825692863d9e57001fba35348..93ca49635731942c72cb52b27b1a6ba53becd1ca
@@@ -320,7 -320,8 +320,9 @@@ static char *prepare_index(int argc, co
                die("unable to write new_index file");
  
        fd = hold_lock_file_for_update(&false_lock,
-                                      git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1);
 -                                     git_path("next-index-%d", getpid()),
++                                     git_path("next-index-%"PRIuMAX,
++                                              (uintmax_t) getpid()),
+                                      LOCK_DIE_ON_ERROR);
  
        create_base_index();
        add_remove_files(&partial);
@@@ -448,7 -449,7 +450,7 @@@ static int prepare_to_commit(const cha
  {
        struct stat statbuf;
        int commitable, saved_color_setting;
 -      struct strbuf sb;
 +      struct strbuf sb = STRBUF_INIT;
        char *buffer;
        FILE *fp;
        const char *hook_arg1 = NULL;
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
  
 -      strbuf_init(&sb, 0);
        if (message.len) {
                strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
                stripspace(&sb, 0);
  
        if (signoff) {
 -              struct strbuf sob;
 +              struct strbuf sob = STRBUF_INIT;
                int i;
  
 -              strbuf_init(&sob, 0);
                strbuf_addstr(&sob, sign_off_header);
                strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
                                             getenv("GIT_COMMITTER_EMAIL")));
  }
  
  /*
 - * Find out if the message starting at position 'start' in the strbuf
 - * contains only whitespace and Signed-off-by lines.
 + * Find out if the message in the strbuf contains only whitespace and
 + * Signed-off-by lines.
   */
 -static int message_is_empty(struct strbuf *sb, int start)
 +static int message_is_empty(struct strbuf *sb)
  {
 -      struct strbuf tmpl;
 +      struct strbuf tmpl = STRBUF_INIT;
        const char *nl;
 -      int eol, i;
 +      int eol, i, start = 0;
  
        if (cleanup_mode == CLEANUP_NONE && sb->len)
                return 0;
  
        /* See if the template is just a prefix of the message. */
 -      strbuf_init(&tmpl, 0);
        if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
                stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
                if (start + tmpl.len <= sb->len &&
        return 1;
  }
  
 +static const char *find_author_by_nickname(const char *name)
 +{
 +      struct rev_info revs;
 +      struct commit *commit;
 +      struct strbuf buf = STRBUF_INIT;
 +      const char *av[20];
 +      int ac = 0;
 +
 +      init_revisions(&revs, NULL);
 +      strbuf_addf(&buf, "--author=%s", name);
 +      av[++ac] = "--all";
 +      av[++ac] = "-i";
 +      av[++ac] = buf.buf;
 +      av[++ac] = NULL;
 +      setup_revisions(ac, av, &revs, NULL);
 +      prepare_revision_walk(&revs);
 +      commit = get_revision(&revs);
 +      if (commit) {
 +              strbuf_release(&buf);
 +              format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
 +              return strbuf_detach(&buf, NULL);
 +      }
 +      die("No existing author found with '%s'", name);
 +}
 +
  static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
                                      const char *prefix)
        logfile = parse_options_fix_filename(prefix, logfile);
        template_file = parse_options_fix_filename(prefix, template_file);
  
 +      if (force_author && !strchr(force_author, '>'))
 +              force_author = find_author_by_nickname(force_author);
 +
        if (logfile || message.len || use_message)
                use_editor = 0;
        if (edit_flag)
@@@ -879,9 -855,6 +881,9 @@@ static void print_summary(const char *p
  {
        struct rev_info rev;
        struct commit *commit;
 +      static const char *format = "format:%h: \"%s\"";
 +      unsigned char junk_sha1[20];
 +      const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
  
        commit = lookup_commit(sha1);
        if (!commit)
  
        rev.verbose_header = 1;
        rev.show_root_diff = 1;
 -      get_commit_format("format:%h: %s", &rev);
 +      get_commit_format(format, &rev);
        rev.always_show_header = 0;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
 -      printf("Created %scommit ", initial_commit ? "initial " : "");
 +      printf("[%s%s]: created ",
 +              !prefixcmp(head, "refs/heads/") ?
 +                      head + 11 :
 +                      !strcmp(head, "HEAD") ?
 +                              "detached HEAD" :
 +                              head,
 +              initial_commit ? " (root-commit)" : "");
  
        if (!log_tree_commit(&rev, commit)) {
                struct strbuf buf = STRBUF_INIT;
 -              format_commit_message(commit, "%h: %s", &buf, DATE_NORMAL);
 +              format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
                printf("%s\n", buf.buf);
                strbuf_release(&buf);
        }
@@@ -935,16 -902,34 +937,16 @@@ static const char commit_utf8_warn[] 
  "You may want to amend it after fixing the message, or set the config\n"
  "variable i18n.commitencoding to the encoding your project uses.\n";
  
 -static void add_parent(struct strbuf *sb, const unsigned char *sha1)
 -{
 -      struct object *obj = parse_object(sha1);
 -      const char *parent = sha1_to_hex(sha1);
 -      const char *cp;
 -
 -      if (!obj)
 -              die("Unable to find commit parent %s", parent);
 -      if (obj->type != OBJ_COMMIT)
 -              die("Parent %s isn't a proper commit", parent);
 -
 -      for (cp = sb->buf; cp && (cp = strstr(cp, "\nparent ")); cp += 8) {
 -              if (!memcmp(cp + 8, parent, 40) && cp[48] == '\n') {
 -                      error("duplicate parent %s ignored", parent);
 -                      return;
 -              }
 -      }
 -      strbuf_addf(sb, "parent %s\n", parent);
 -}
 -
  int cmd_commit(int argc, const char **argv, const char *prefix)
  {
 -      int header_len;
 -      struct strbuf sb;
 +      struct strbuf sb = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl, *p;
        unsigned char commit_sha1[20];
        struct ref_lock *ref_lock;
 +      struct commit_list *parents = NULL, **pptr = &parents;
 +      struct stat statbuf;
 +      int allow_fast_forward = 1;
  
        git_config(git_commit_config, NULL);
  
                return 1;
        }
  
 -      /*
 -       * The commit object
 -       */
 -      strbuf_init(&sb, 0);
 -      strbuf_addf(&sb, "tree %s\n",
 -                  sha1_to_hex(active_cache_tree->sha1));
 -
        /* Determine parents */
        if (initial_commit) {
                reflog_msg = "commit (initial)";
                        die("could not parse HEAD commit");
  
                for (c = commit->parents; c; c = c->next)
 -                      add_parent(&sb, c->item->object.sha1);
 +                      pptr = &commit_list_insert(c->item, pptr)->next;
        } else if (in_merge) {
 -              struct strbuf m;
 +              struct strbuf m = STRBUF_INIT;
                FILE *fp;
  
                reflog_msg = "commit (merge)";
 -              add_parent(&sb, head_sha1);
 -              strbuf_init(&m, 0);
 +              pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
                        die("could not open %s for reading: %s",
                        unsigned char sha1[20];
                        if (get_sha1_hex(m.buf, sha1) < 0)
                                die("Corrupt MERGE_HEAD file (%s)", m.buf);
 -                      add_parent(&sb, sha1);
 +                      pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next;
                }
                fclose(fp);
                strbuf_release(&m);
 +              if (!stat(git_path("MERGE_MODE"), &statbuf)) {
 +                      if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
 +                              die("could not read MERGE_MODE: %s",
 +                                              strerror(errno));
 +                      if (!strcmp(sb.buf, "no-ff"))
 +                              allow_fast_forward = 0;
 +              }
 +              if (allow_fast_forward)
 +                      parents = reduce_heads(parents);
        } else {
                reflog_msg = "commit";
 -              strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
 +              pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
  
 -      strbuf_addf(&sb, "author %s\n",
 -                  fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME));
 -      strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
 -      if (!is_encoding_utf8(git_commit_encoding))
 -              strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
 -      strbuf_addch(&sb, '\n');
 -
        /* Finally, get the commit message */
 -      header_len = sb.len;
 +      strbuf_reset(&sb);
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
                rollback_index_files();
                die("could not read commit message");
  
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
 -      if (sb.len < header_len || message_is_empty(&sb, header_len)) {
 +      if (message_is_empty(&sb)) {
                rollback_index_files();
                fprintf(stderr, "Aborting commit due to empty commit message.\n");
                exit(1);
        }
 -      strbuf_addch(&sb, '\0');
 -      if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
 -              fprintf(stderr, commit_utf8_warn);
  
 -      if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {
 +      if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
 +                      fmt_ident(author_name, author_email, author_date,
 +                              IDENT_ERROR_ON_NO_NAME))) {
                rollback_index_files();
                die("failed to write commit object");
        }
                                           initial_commit ? NULL : head_sha1,
                                           0);
  
 -      nl = strchr(sb.buf + header_len, '\n');
 +      nl = strchr(sb.buf, '\n');
        if (nl)
                strbuf_setlen(&sb, nl + 1 - sb.buf);
        else
                strbuf_addch(&sb, '\n');
 -      strbuf_remove(&sb, 0, header_len);
        strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
        strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
  
  
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
 +      unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
  
        if (commit_index_files())
diff --combined builtin-fetch-pack.c
index fa3c936493cc0b139edf3e4e8154569f453afc02,21ce3e016314e0c8500c9010b2ff214b1e05e022..372bfa20a2eac978f7511f5d8d9296be789b527f
@@@ -540,7 -540,7 +540,7 @@@ static int get_pack(int xd[2], char **p
                        *av++ = "--fix-thin";
                if (args.lock_pack || unpack_limit) {
                        int s = sprintf(keep_arg,
 -                                      "--keep=fetch-pack %d on ", getpid());
 +                                      "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
                        if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                                strcpy(keep_arg + s, "localhost");
                        *av++ = keep_arg;
@@@ -735,7 -735,7 +735,7 @@@ int cmd_fetch_pack(int argc, const cha
        conn = git_connect(fd, (char *)dest, args.uploadpack,
                           args.verbose ? CONNECT_VERBOSE : 0);
        if (conn) {
 -              get_remote_heads(fd[0], &ref, 0, NULL, 0);
 +              get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
  
                ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
                close(fd[0]);
@@@ -813,7 -813,8 +813,8 @@@ struct ref *fetch_pack(struct fetch_pac
                          )
                        die("shallow file was changed during fetch");
  
-               fd = hold_lock_file_for_update(&lock, shallow, 1);
+               fd = hold_lock_file_for_update(&lock, shallow,
+                                              LOCK_DIE_ON_ERROR);
                if (!write_shallow_commits(fd, 0)) {
                        unlink(shallow);
                        rollback_lock_file(&lock);
diff --combined builtin-revert.c
index 84865397405d96aa4fdbfca801e8ecd47e3dea36,e839387629461be24c0bf1dacaa3cdb48cb2a5f4..472554019afdb5e8e21f2d362f62f40eb569d8aa
@@@ -11,8 -11,6 +11,8 @@@
  #include "cache-tree.h"
  #include "diff.h"
  #include "revision.h"
 +#include "rerere.h"
 +#include "merge-recursive.h"
  
  /*
   * This implements the builtins revert and cherry-pick.
@@@ -202,6 -200,36 +202,6 @@@ static void set_author_ident_env(const 
                        sha1_to_hex(commit->object.sha1));
  }
  
 -static int merge_recursive(const char *base_sha1,
 -              const char *head_sha1, const char *head_name,
 -              const char *next_sha1, const char *next_name)
 -{
 -      char buffer[256];
 -      const char *argv[6];
 -      int i = 0;
 -
 -      sprintf(buffer, "GITHEAD_%s", head_sha1);
 -      setenv(buffer, head_name, 1);
 -      sprintf(buffer, "GITHEAD_%s", next_sha1);
 -      setenv(buffer, next_name, 1);
 -
 -      /*
 -       * This three way merge is an interesting one.  We are at
 -       * $head, and would want to apply the change between $commit
 -       * and $prev on top of us (when reverting), or the change between
 -       * $prev and $commit on top of us (when cherry-picking or replaying).
 -       */
 -      argv[i++] = "merge-recursive";
 -      if (base_sha1)
 -              argv[i++] = base_sha1;
 -      argv[i++] = "--";
 -      argv[i++] = head_sha1;
 -      argv[i++] = next_sha1;
 -      argv[i++] = NULL;
 -
 -      return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
 -}
 -
  static char *help_msg(const unsigned char *sha1)
  {
        static char helpbuf[1024];
@@@ -234,27 -262,14 +234,27 @@@ static int index_is_dirty(void
        return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
  }
  
 +static struct tree *empty_tree(void)
 +{
 +      struct tree *tree = xcalloc(1, sizeof(struct tree));
 +
 +      tree->object.parsed = 1;
 +      tree->object.type = OBJ_TREE;
 +      pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
 +      return tree;
 +}
 +
  static int revert_or_cherry_pick(int argc, const char **argv)
  {
        unsigned char head[20];
        struct commit *base, *next, *parent;
 -      int i;
 +      int i, index_fd, clean;
        char *oneline, *reencoded_message = NULL;
        const char *message, *encoding;
        const char *defmsg = xstrdup(git_path("MERGE_MSG"));
 +      struct merge_options o;
 +      struct tree *result, *next_tree, *base_tree, *head_tree;
 +      static struct lock_file index_lock;
  
        git_config(git_default_config, NULL);
        me = action == REVERT ? "revert" : "cherry-pick";
        if (action == REVERT && !no_replay)
                die("revert is incompatible with replay");
  
 +      if (read_cache() < 0)
 +              die("git %s: failed to read the index", me);
        if (no_commit) {
                /*
                 * We do not intend to commit immediately.  We just want to
        } else {
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
 -              if (read_cache() < 0)
 -                      die("could not read the index");
                if (index_is_dirty())
                        die ("Dirty index: cannot %s", me);
 -              discard_cache();
        }
 +      discard_cache();
 +
 +      index_fd = hold_locked_index(&index_lock, 1);
  
        if (!commit->parents) {
                if (action == REVERT)
                die ("Cannot get commit message for %s",
                                sha1_to_hex(commit->object.sha1));
  
 +      if (parent && parse_commit(parent) < 0)
 +              die("%s: cannot parse parent commit %s",
 +                  me, sha1_to_hex(parent->object.sha1));
 +
        /*
         * "commit" is an existing commit.  We would want to apply
         * the difference it introduces since its first parent "prev"
         * reverse of it if we are revert.
         */
  
-       msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1);
+       msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
+                                          LOCK_DIE_ON_ERROR);
  
        encoding = get_encoding(message);
        if (!encoding)
                }
        }
  
 -      if (merge_recursive(base == NULL ?
 -                              NULL : sha1_to_hex(base->object.sha1),
 -                              sha1_to_hex(head), "HEAD",
 -                              sha1_to_hex(next->object.sha1), oneline) ||
 -                      write_cache_as_tree(head, 0, NULL)) {
 +      read_cache();
 +      init_merge_options(&o);
 +      o.branch1 = "HEAD";
 +      o.branch2 = oneline;
 +
 +      head_tree = parse_tree_indirect(head);
 +      next_tree = next ? next->tree : empty_tree();
 +      base_tree = base ? base->tree : empty_tree();
 +
 +      clean = merge_trees(&o,
 +                          head_tree,
 +                          next_tree, base_tree, &result);
 +
 +      if (active_cache_changed &&
 +          (write_cache(index_fd, active_cache, active_nr) ||
 +           commit_locked_index(&index_lock)))
 +              die("%s: Unable to write new index file", me);
 +
 +      if (!clean) {
                add_to_msg("\nConflicts:\n\n");
 -              read_cache();
                for (i = 0; i < active_nr;) {
                        struct cache_entry *ce = active_cache[i++];
                        if (ce_stage(ce)) {
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
                        me, help_msg(commit->object.sha1));
 +              rerere();
                exit(1);
        }
        if (commit_lock_file(&msg_file) < 0)
diff --combined cache.h
index ae6647e22c0b6811c5f932b06622a75bd97b0a22,8ab2fd8fd082bc0c263fc055cab8e248b56985e0..cdbeb48c65f45b4b37ceb767a72866e5a7475cc3
+++ b/cache.h
@@@ -6,14 -6,8 +6,14 @@@
  #include "hash.h"
  
  #include SHA1_HEADER
 -#include <zlib.h>
 +#ifndef git_SHA_CTX
 +#define git_SHA_CTX   SHA_CTX
 +#define git_SHA1_Init SHA1_Init
 +#define git_SHA1_Update       SHA1_Update
 +#define git_SHA1_Final        SHA1_Final
 +#endif
  
 +#include <zlib.h>
  #if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
  #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
  #endif
@@@ -132,7 -126,6 +132,7 @@@ struct cache_entry 
  
  #define CE_NAMEMASK  (0x0fff)
  #define CE_STAGEMASK (0x3000)
 +#define CE_EXTENDED  (0x4000)
  #define CE_VALID     (0x8000)
  #define CE_STAGESHIFT 12
  
@@@ -277,7 -270,6 +277,7 @@@ static inline void remove_name_hash(str
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 +#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #endif
  
  enum object_type {
@@@ -320,7 -312,6 +320,7 @@@ extern int is_bare_repository(void)
  extern int is_inside_git_dir(void);
  extern char *git_work_tree_cfg;
  extern int is_inside_work_tree(void);
 +extern int have_git_dir(void);
  extern const char *get_git_dir(void);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
@@@ -379,7 -370,6 +379,7 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
 +#define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
@@@ -388,13 -378,10 +388,13 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_PRETEND 2
  #define ADD_CACHE_IGNORE_ERRORS       4
 +#define ADD_CACHE_IGNORE_REMOVAL 8
 +#define ADD_CACHE_INTENT 16
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 +extern int index_name_is_other(const struct index_state *, const char *, int);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
@@@ -405,6 -392,7 +405,6 @@@ extern int ie_modified(const struct ind
  
  extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 -extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
@@@ -423,6 -411,8 +423,8 @@@ struct lock_file 
        char on_list;
        char filename[PATH_MAX];
  };
+ #define LOCK_DIE_ON_ERROR 1
+ #define LOCK_NODEREF 2
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
@@@ -464,7 -454,6 +466,7 @@@ enum safe_crlf 
  extern enum safe_crlf safe_crlf;
  
  enum branch_track {
 +      BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
@@@ -544,7 -533,6 +546,7 @@@ static inline int is_absolute_path(cons
  {
        return path[0] == '/' || has_dos_drive_prefix(path);
  }
 +int is_directory(const char *);
  const char *make_absolute_path(const char *path);
  const char *make_nonrelative_path(const char *path);
  const char *make_relative_path(const char *abs, const char *base);
@@@ -652,8 -640,6 +654,8 @@@ extern struct alternate_object_databas
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void add_to_alternates_file(const char *reference);
 +typedef int alt_odb_fn(struct alternate_object_database *, void *);
 +extern void foreach_alt_odb(alt_odb_fn, void*);
  
  struct pack_window {
        struct pack_window *next;
@@@ -722,11 -708,7 +724,11 @@@ extern struct child_process *git_connec
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  extern int get_ack(int fd, unsigned char *result_sha1);
 -extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
 +struct extra_have_objects {
 +      int nr, alloc;
 +      unsigned char (*array)[20];
 +};
 +extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1);
@@@ -760,6 -742,7 +762,6 @@@ typedef int (*config_fn_t)(const char *
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern int git_config(config_fn_t fn, void *);
 -extern int git_parse_long(const char *, long *);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
diff --combined refs.c
index b6807505e243fd26cae50460bc003254406ae7e5,9e422dcccbb3d499b55df5f39612b12c3c3f52e0..0a126fa371ae24f5cb251ff52cf7d38322d78ba7
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -390,18 -390,6 +390,18 @@@ int resolve_gitlink_ref(const char *pat
        return retval;
  }
  
 +/*
 + * If the "reading" argument is set, this function finds out what _object_
 + * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
 + * has to exist, and if it is symbolic, it has to point at an existing ref,
 + * because the "read" goes through the symref to the ref it points at.
 + *
 + * The access that is not "reading" may often be "writing", but does not
 + * have to; it can be merely checking _where it leads to_. If it is a
 + * prelude to "writing" to the ref, a write to a symref that points at
 + * yet-to-be-born ref will create the real ref pointed by the symref.
 + * reading=0 allows the caller to check where such a symref leads to.
 + */
  const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
  {
        int depth = MAXDEPTH;
                if (--depth < 0)
                        return NULL;
  
 -              /* Special case: non-existing file.
 -               * Not having the refs/heads/new-branch is OK
 -               * if we are writing into it, so is .git/HEAD
 -               * that points at refs/heads/master still to be
 -               * born.  It is NOT OK if we are resolving for
 -               * reading.
 -               */
 +              /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
                        struct ref_list *list = get_packed_refs();
                        while (list) {
@@@ -796,7 -790,7 +796,7 @@@ static struct ref_lock *lock_ref_sha1_b
        struct ref_lock *lock;
        struct stat st;
        int last_errno = 0;
-       int type;
+       int type, lflags;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
  
        lock = xcalloc(1, sizeof(struct ref_lock));
  
        lock->lk = xcalloc(1, sizeof(struct lock_file));
  
-       if (flags & REF_NODEREF)
+       lflags = LOCK_DIE_ON_ERROR;
+       if (flags & REF_NODEREF) {
                ref = orig_ref;
+               lflags |= LOCK_NODEREF;
+       }
        lock->ref_name = xstrdup(ref);
        lock->orig_ref_name = xstrdup(orig_ref);
        ref_file = git_path("%s", ref);
                error("unable to create directory for %s", ref_file);
                goto error_return;
        }
-       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
  
+       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
  
   error_return:
diff --combined rerere.c
index 5bb5316cdae16154fd4aaffe996710c0bcfc0b94,2b7a99d729e13b09239f1d9175d3d6c0048821b0..8e5532b1ff96a88133d837b9a29fa78117c36abd
+++ b/rerere.c
@@@ -73,13 -73,10 +73,13 @@@ static int write_rr(struct string_list 
  static int handle_file(const char *path,
         unsigned char *sha1, const char *output)
  {
 -      SHA_CTX ctx;
 +      git_SHA_CTX ctx;
        char buf[1024];
 -      int hunk = 0, hunk_no = 0;
 -      struct strbuf one, two;
 +      int hunk_no = 0;
 +      enum {
 +              RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
 +      } hunk = RR_CONTEXT;
 +      struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
        FILE *f = fopen(path, "r");
        FILE *out = NULL;
  
        }
  
        if (sha1)
 -              SHA1_Init(&ctx);
 +              git_SHA1_Init(&ctx);
  
 -      strbuf_init(&one, 0);
 -      strbuf_init(&two,  0);
        while (fgets(buf, sizeof(buf), f)) {
                if (!prefixcmp(buf, "<<<<<<< ")) {
 -                      if (hunk)
 +                      if (hunk != RR_CONTEXT)
                                goto bad;
 -                      hunk = 1;
 +                      hunk = RR_SIDE_1;
 +              } else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) {
 +                      if (hunk != RR_SIDE_1)
 +                              goto bad;
 +                      hunk = RR_ORIGINAL;
                } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
 -                      if (hunk != 1)
 +                      if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
                                goto bad;
 -                      hunk = 2;
 +                      hunk = RR_SIDE_2;
                } else if (!prefixcmp(buf, ">>>>>>> ")) {
 -                      if (hunk != 2)
 +                      if (hunk != RR_SIDE_2)
                                goto bad;
                        if (strbuf_cmp(&one, &two) > 0)
                                strbuf_swap(&one, &two);
                        hunk_no++;
 -                      hunk = 0;
 +                      hunk = RR_CONTEXT;
                        if (out) {
                                fputs("<<<<<<<\n", out);
                                fwrite(one.buf, one.len, 1, out);
                                fputs(">>>>>>>\n", out);
                        }
                        if (sha1) {
 -                              SHA1_Update(&ctx, one.buf ? one.buf : "",
 +                              git_SHA1_Update(&ctx, one.buf ? one.buf : "",
                                            one.len + 1);
 -                              SHA1_Update(&ctx, two.buf ? two.buf : "",
 +                              git_SHA1_Update(&ctx, two.buf ? two.buf : "",
                                            two.len + 1);
                        }
                        strbuf_reset(&one);
                        strbuf_reset(&two);
 -              } else if (hunk == 1)
 +              } else if (hunk == RR_SIDE_1)
                        strbuf_addstr(&one, buf);
 -              else if (hunk == 2)
 +              else if (hunk == RR_ORIGINAL)
 +                      ; /* discard */
 +              else if (hunk == RR_SIDE_2)
                        strbuf_addstr(&two, buf);
                else if (out)
                        fputs(buf, out);
        if (out)
                fclose(out);
        if (sha1)
 -              SHA1_Final(sha1, &ctx);
 -      if (hunk) {
 +              git_SHA1_Final(sha1, &ctx);
 +      if (hunk != RR_CONTEXT) {
                if (output)
                        unlink(output);
                return error("Could not parse conflict hunks in %s", path);
@@@ -326,6 -319,7 +326,6 @@@ static int git_rerere_config(const cha
  
  static int is_rerere_enabled(void)
  {
 -      struct stat st;
        const char *rr_cache;
        int rr_cache_exists;
  
                return 0;
  
        rr_cache = git_path("rr-cache");
 -      rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
 +      rr_cache_exists = is_directory(rr_cache);
        if (rerere_enabled < 0)
                return rr_cache_exists;
  
@@@ -352,7 -346,8 +352,8 @@@ int setup_rerere(struct string_list *me
                return -1;
  
        merge_rr_path = xstrdup(git_path("MERGE_RR"));
-       fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
+       fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
+                                      LOCK_DIE_ON_ERROR);
        read_rr(merge_rr);
        return fd;
  }
diff --combined sha1_file.c
index 70bb453be25d4e5ba05ee5f2565630a8994842f2,5cfae5d1adb8c386d4e38c1960f60fb3357c17b3..ab2b520f033c75e5e8d3434376a203add59559a4
@@@ -99,11 -99,7 +99,11 @@@ int safe_create_leading_directories(cha
                pos = strchr(pos, '/');
                if (!pos)
                        break;
 -              *pos = 0;
 +              while (*++pos == '/')
 +                      ;
 +              if (!*pos)
 +                      break;
 +              *--pos = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
                        if (!S_ISDIR(st.st_mode)) {
@@@ -254,6 -250,7 +254,6 @@@ static void read_info_alternates(const 
   */
  static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
  {
 -      struct stat st;
        const char *objdir = get_object_directory();
        struct alternate_object_database *ent;
        struct alternate_object_database *alt;
        ent->base[pfxlen] = ent->base[entlen-1] = 0;
  
        /* Detect cases where alternate disappeared */
 -      if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
 +      if (!is_directory(ent->base)) {
                error("object directory %s does not exist; "
                      "check .git/objects/info/alternates.",
                      ent->base);
@@@ -388,7 -385,7 +388,7 @@@ static void read_info_alternates(const 
  void add_to_alternates_file(const char *reference)
  {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1);
+       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
        char *alt = mkpath("%s/objects\n", reference);
        write_or_die(fd, alt, strlen(alt));
        if (commit_lock_file(lock))
                link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
  }
  
 +void foreach_alt_odb(alt_odb_fn fn, void *cb)
 +{
 +      struct alternate_object_database *ent;
 +
 +      prepare_alt_odb();
 +      for (ent = alt_odb_list; ent; ent = ent->next)
 +              if (fn(ent, cb))
 +                      return;
 +}
 +
  void prepare_alt_odb(void)
  {
        const char *alt;
@@@ -1571,9 -1558,11 +1571,9 @@@ static void *cache_or_unpack_entry(stru
        struct delta_base_cache_entry *ent = delta_base_cache + hash;
  
        ret = ent->data;
 -      if (ret && ent->p == p && ent->base_offset == base_offset)
 -              goto found_cache_entry;
 -      return unpack_entry(p, base_offset, type, base_size);
 +      if (!ret || ent->p != p || ent->base_offset != base_offset)
 +              return unpack_entry(p, base_offset, type, base_size);
  
 -found_cache_entry:
        if (!keep_cache) {
                ent->data = NULL;
                ent->lru.next->prev = ent->lru.prev;
@@@ -2130,16 -2119,16 +2130,16 @@@ static void write_sha1_file_prepare(con
                                      const char *type, unsigned char *sha1,
                                      char *hdr, int *hdrlen)
  {
 -      SHA_CTX c;
 +      git_SHA_CTX c;
  
        /* Generate the header */
        *hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
  
        /* Sha1.. */
 -      SHA1_Init(&c);
 -      SHA1_Update(&c, hdr, *hdrlen);
 -      SHA1_Update(&c, buf, len);
 -      SHA1_Final(sha1, &c);
 +      git_SHA1_Init(&c);
 +      git_SHA1_Update(&c, hdr, *hdrlen);
 +      git_SHA1_Update(&c, buf, len);
 +      git_SHA1_Final(sha1, &c);
  }
  
  /*
@@@ -2333,7 -2322,6 +2333,7 @@@ int force_object_loose(const unsigned c
        enum object_type type;
        char hdr[32];
        int hdrlen;
 +      int ret;
  
        if (has_loose_object(sha1))
                return 0;
        if (!buf)
                return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
        hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
 -      return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
 +      ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
 +      free(buf);
 +
 +      return ret;
  }
  
  int has_pack_index(const unsigned char *sha1)
@@@ -2378,21 -2363,51 +2378,21 @@@ int has_sha1_file(const unsigned char *
        return has_loose_object(sha1);
  }
  
 -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
 -{
 -      struct strbuf buf;
 -      int ret;
 -
 -      strbuf_init(&buf, 0);
 -      if (strbuf_read(&buf, fd, 4096) < 0) {
 -              strbuf_release(&buf);
 -              return -1;
 -      }
 -
 -      if (!type)
 -              type = blob_type;
 -      if (write_object)
 -              ret = write_sha1_file(buf.buf, buf.len, type, sha1);
 -      else
 -              ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
 -      strbuf_release(&buf);
 -
 -      return ret;
 -}
 -
 -int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
 -           enum object_type type, const char *path)
 +static int index_mem(unsigned char *sha1, void *buf, size_t size,
 +                   int write_object, enum object_type type, const char *path)
  {
 -      size_t size = xsize_t(st->st_size);
 -      void *buf = NULL;
        int ret, re_allocated = 0;
  
 -      if (size)
 -              buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -      close(fd);
 -
        if (!type)
                type = OBJ_BLOB;
  
        /*
         * Convert blobs to git internal format
         */
 -      if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
 -              struct strbuf nbuf;
 -              strbuf_init(&nbuf, 0);
 +      if ((type == OBJ_BLOB) && path) {
 +              struct strbuf nbuf = STRBUF_INIT;
                if (convert_to_git(path, buf, size, &nbuf,
                                   write_object ? safe_crlf : 0)) {
 -                      munmap(buf, size);
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
                ret = write_sha1_file(buf, size, typename(type), sha1);
        else
                ret = hash_sha1_file(buf, size, typename(type), sha1);
 -      if (re_allocated) {
 +      if (re_allocated)
                free(buf);
 -              return ret;
 -      }
 -      if (size)
 +      return ret;
 +}
 +
 +int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
 +           enum object_type type, const char *path)
 +{
 +      int ret;
 +      size_t size = xsize_t(st->st_size);
 +
 +      if (!S_ISREG(st->st_mode)) {
 +              struct strbuf sbuf = STRBUF_INIT;
 +              if (strbuf_read(&sbuf, fd, 4096) >= 0)
 +                      ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
 +                                      type, path);
 +              else
 +                      ret = -1;
 +              strbuf_release(&sbuf);
 +      } else if (size) {
 +              void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 +              ret = index_mem(sha1, buf, size, write_object, type, path);
                munmap(buf, size);
 +      } else
 +              ret = index_mem(sha1, NULL, size, write_object, type, path);
 +      close(fd);
        return ret;
  }
  
diff --combined t/t7201-co.sh
index 82769b89fc79d91982420ddfc83ce6c3dd672095,f93478f97ff8d139d4c67690e1c85f098ca90a10..0e21632f19e75e1e7bbca1ceb2b9402a4d291584
@@@ -330,46 -330,28 +330,60 @@@ test_expect_success 
      test "$(git config branch.track2.merge)"
      git config branch.autosetupmerge false'
  
- test_expect_success \
-     'checkout w/--track from non-branch HEAD fails' '
-     git checkout -b delete-me master &&
-     rm .git/refs/heads/delete-me &&
-     test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
-     test_must_fail git checkout --track -b track'
+ test_expect_success 'checkout w/--track from non-branch HEAD fails' '
+     git checkout master^0 &&
+     test_must_fail git symbolic-ref HEAD &&
+     test_must_fail git checkout --track -b track &&
+     test_must_fail git rev-parse --verify track &&
+     test_must_fail git symbolic-ref HEAD &&
+     test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+ '
+ test_expect_success 'detach a symbolic link HEAD' '
+     git checkout master &&
+     git config --bool core.prefersymlinkrefs yes &&
+     git checkout side &&
+     git checkout master &&
+     it=$(git symbolic-ref HEAD) &&
+     test "z$it" = zrefs/heads/master &&
+     here=$(git rev-parse --verify refs/heads/master) &&
+     git checkout side^ &&
+     test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
+ '
  
 -test_expect_success 'checkout an unmerged path should fail' '
 +test_expect_success \
 +    'checkout with --track fakes a sensible -b <name>' '
 +    git update-ref refs/remotes/origin/koala/bear renamer &&
 +    git update-ref refs/new/koala/bear renamer &&
 +
 +    git checkout --track origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track refs/remotes/origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track remotes/origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track refs/new/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 +'
 +
 +test_expect_success \
 +    'checkout with --track, but without -b, fails with too short tracked name' '
 +    test_must_fail git checkout --track renamer'
 +
 +setup_conflicting_index () {
        rm -f .git/index &&
        O=$(echo original | git hash-object -w --stdin) &&
        A=$(echo ourside | git hash-object -w --stdin) &&
                echo "100644 $A 2       file" &&
                echo "100644 $B 3       file" &&
                echo "100644 $A 0       filf"
 -      ) | git update-index --index-info &&
 +      ) | git update-index --index-info
 +}
 +
 +test_expect_success 'checkout an unmerged path should fail' '
 +      setup_conflicting_index &&
        echo "none of the above" >sample &&
        cat sample >fild &&
        cat sample >file &&
        test_cmp sample file
  '
  
 +test_expect_success 'checkout with an unmerged path can be ignored' '
 +      setup_conflicting_index &&
 +      echo "none of the above" >sample &&
 +      echo ourside >expect &&
 +      cat sample >fild &&
 +      cat sample >file &&
 +      cat sample >filf &&
 +      git checkout -f fild file filf &&
 +      test_cmp expect fild &&
 +      test_cmp expect filf &&
 +      test_cmp sample file
 +'
 +
 +test_expect_success 'checkout unmerged stage' '
 +      setup_conflicting_index &&
 +      echo "none of the above" >sample &&
 +      echo ourside >expect &&
 +      cat sample >fild &&
 +      cat sample >file &&
 +      cat sample >filf &&
 +      git checkout --ours . &&
 +      test_cmp expect fild &&
 +      test_cmp expect filf &&
 +      test_cmp expect file &&
 +      git checkout --theirs file &&
 +      test ztheirside = "z$(cat file)"
 +'
 +
 +test_expect_success 'checkout with --merge' '
 +      setup_conflicting_index &&
 +      echo "none of the above" >sample &&
 +      echo ourside >expect &&
 +      cat sample >fild &&
 +      cat sample >file &&
 +      cat sample >filf &&
 +      git checkout -m -- fild file filf &&
 +      (
 +              echo "<<<<<<< ours"
 +              echo ourside
 +              echo "======="
 +              echo theirside
 +              echo ">>>>>>> theirs"
 +      ) >merged &&
 +      test_cmp expect fild &&
 +      test_cmp expect filf &&
 +      test_cmp merged file
 +'
 +
 +test_expect_success 'checkout with --merge, in diff3 -m style' '
 +      git config merge.conflictstyle diff3 &&
 +      setup_conflicting_index &&
 +      echo "none of the above" >sample &&
 +      echo ourside >expect &&
 +      cat sample >fild &&
 +      cat sample >file &&
 +      cat sample >filf &&
 +      git checkout -m -- fild file filf &&
 +      (
 +              echo "<<<<<<< ours"
 +              echo ourside
 +              echo "|||||||"
 +              echo original
 +              echo "======="
 +              echo theirside
 +              echo ">>>>>>> theirs"
 +      ) >merged &&
 +      test_cmp expect fild &&
 +      test_cmp expect filf &&
 +      test_cmp merged file
 +'
 +
 +test_expect_success 'checkout --conflict=merge, overriding config' '
 +      git config merge.conflictstyle diff3 &&
 +      setup_conflicting_index &&
 +      echo "none of the above" >sample &&
 +      echo ourside >expect &&
 +      cat sample >fild &&
 +      cat sample >file &&
 +      cat sample >filf &&
 +      git checkout --conflict=merge -- fild file filf &&
 +      (
 +              echo "<<<<<<< ours"
 +              echo ourside
 +              echo "======="
 +              echo theirside
 +              echo ">>>>>>> theirs"
 +      ) >merged &&
 +      test_cmp expect fild &&
 +      test_cmp expect filf &&
 +      test_cmp merged file
 +'
 +
 +test_expect_success 'checkout --conflict=diff3' '
 +      git config --unset merge.conflictstyle
 +      setup_conflicting_index &&
 +      echo "none of the above" >sample &&
 +      echo ourside >expect &&
 +      cat sample >fild &&
 +      cat sample >file &&
 +      cat sample >filf &&
 +      git checkout --conflict=diff3 -- fild file filf &&
 +      (
 +              echo "<<<<<<< ours"
 +              echo ourside
 +              echo "|||||||"
 +              echo original
 +              echo "======="
 +              echo theirside
 +              echo ">>>>>>> theirs"
 +      ) >merged &&
 +      test_cmp expect fild &&
 +      test_cmp expect filf &&
 +      test_cmp merged file
 +'
 +
 +test_expect_success 'failing checkout -b should not break working tree' '
 +      git reset --hard master &&
 +      git symbolic-ref HEAD refs/heads/master &&
 +      test_must_fail git checkout -b renamer side^ &&
 +      test $(git symbolic-ref HEAD) = refs/heads/master &&
 +      git diff --exit-code &&
 +      git diff --cached --exit-code
 +
 +'
 +
  test_done