OSDN Git Service

Merge branch 'ew/empty-merge-with-dirty-index-maint' into ew/empty-merge-with-dirty...
authorJunio C Hamano <gitster@pobox.com>
Fri, 22 Dec 2017 20:48:38 +0000 (12:48 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 22 Dec 2017 20:48:38 +0000 (12:48 -0800)
* ew/empty-merge-with-dirty-index-maint:
  merge-recursive: avoid incorporating uncommitted changes in a merge
  move index_has_changes() from builtin/am.c to merge.c for reuse
  t6044: recursive can silently incorporate dirty changes in a merge

1  2 
builtin/am.c
cache.h
merge-recursive.c
merge.c

diff --combined builtin/am.c
@@@ -1068,8 -1068,8 +1068,8 @@@ static void am_setup(struct am_state *s
        if (!get_oid("HEAD", &curr_head)) {
                write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
                if (!state->rebasing)
 -                      update_ref_oid("am", "ORIG_HEAD", &curr_head, NULL, 0,
 -                                      UPDATE_REFS_DIE_ON_ERR);
 +                      update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
 +                                 UPDATE_REFS_DIE_ON_ERR);
        } else {
                write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
@@@ -1134,52 -1134,15 +1134,15 @@@ static const char *msgnum(const struct 
   */
  static void refresh_and_write_cache(void)
  {
 -      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
  
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write index file"));
  }
  
  /**
-  * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
-  * branch, returns 1 if there are entries in the index, 0 otherwise. If an
-  * strbuf is provided, the space-separated list of files that differ will be
-  * appended to it.
-  */
- static int index_has_changes(struct strbuf *sb)
- {
-       struct object_id head;
-       int i;
-       if (!get_oid_tree("HEAD", &head)) {
-               struct diff_options opt;
-               diff_setup(&opt);
-               opt.flags.exit_with_status = 1;
-               if (!sb)
-                       opt.flags.quick = 1;
-               do_diff_cache(&head, &opt);
-               diffcore_std(&opt);
-               for (i = 0; sb && i < diff_queued_diff.nr; i++) {
-                       if (i)
-                               strbuf_addch(sb, ' ');
-                       strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
-               }
-               diff_flush(&opt);
-               return opt.flags.has_changes != 0;
-       } else {
-               for (i = 0; sb && i < active_nr; i++) {
-                       if (i)
-                               strbuf_addch(sb, ' ');
-                       strbuf_addstr(sb, active_cache[i]->name);
-               }
-               return !!active_nr;
-       }
- }
- /**
   * Dies with a user-friendly message on how to proceed after resolving the
   * problem. This message can be overridden with state->resolvemsg.
   */
@@@ -1409,8 -1372,8 +1372,8 @@@ static void write_commit_patch(const st
        rev_info.show_root_diff = 1;
        rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev_info.no_commit_id = 1;
 -      DIFF_OPT_SET(&rev_info.diffopt, BINARY);
 -      DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
 +      rev_info.diffopt.flags.binary = 1;
 +      rev_info.diffopt.flags.full_index = 1;
        rev_info.diffopt.use_color = 0;
        rev_info.diffopt.file = fp;
        rev_info.diffopt.close_file = 1;
@@@ -1433,7 -1396,7 +1396,7 @@@ static void write_index_patch(const str
        if (!get_oid_tree("HEAD", &head))
                tree = lookup_tree(&head);
        else
 -              tree = lookup_tree(&empty_tree_oid);
 +              tree = lookup_tree(the_hash_algo->empty_tree);
  
        fp = xfopen(am_path(state, "patch"), "w");
        init_revisions(&rev_info, NULL);
@@@ -1488,10 -1451,11 +1451,10 @@@ static int run_apply(const struct am_st
        struct argv_array apply_opts = ARGV_ARRAY_INIT;
        struct apply_state apply_state;
        int res, opts_left;
 -      static struct lock_file lock_file;
        int force_apply = 0;
        int options = 0;
  
 -      if (init_apply_state(&apply_state, NULL, &lock_file))
 +      if (init_apply_state(&apply_state, NULL))
                die("BUG: init_apply_state() failed");
  
        argv_array_push(&apply_opts, "apply");
@@@ -1685,8 -1649,8 +1648,8 @@@ static void do_commit(const struct am_s
        strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
                        state->msg);
  
 -      update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 0,
 -                      UPDATE_REFS_DIE_ON_ERR);
 +      update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
 +                 UPDATE_REFS_DIE_ON_ERR);
  
        if (state->rebasing) {
                FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@@ -1945,14 -1909,15 +1908,14 @@@ next
   */
  static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[2];
  
        if (parse_tree(head) || parse_tree(remote))
                return -1;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        refresh_cache(REFRESH_QUIET);
  
        init_tree_desc(&t[1], remote->buffer, remote->size);
  
        if (unpack_trees(2, t, &opts)) {
 -              rollback_lock_file(lock_file);
 +              rollback_lock_file(&lock_file);
                return -1;
        }
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        return 0;
   */
  static int merge_tree(struct tree *tree)
  {
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[1];
  
        if (parse_tree(tree))
                return -1;
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
  
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        init_tree_desc(&t[0], tree->buffer, tree->size);
  
        if (unpack_trees(1, t, &opts)) {
 -              rollback_lock_file(lock_file);
 +              rollback_lock_file(&lock_file);
                return -1;
        }
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        return 0;
@@@ -2132,7 -2098,7 +2095,7 @@@ static void am_abort(struct am_state *s
  
        am_rerere_clear();
  
 -      curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
 +      curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
        has_curr_head = curr_branch && !is_null_oid(&curr_head);
        if (!has_curr_head)
                hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
        clean_index(&curr_head, &orig_head);
  
        if (has_orig_head)
 -              update_ref_oid("am --abort", "HEAD", &orig_head,
 -                              has_curr_head ? &curr_head : NULL, 0,
 -                              UPDATE_REFS_DIE_ON_ERR);
 +              update_ref("am --abort", "HEAD", &orig_head,
 +                         has_curr_head ? &curr_head : NULL, 0,
 +                         UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
 -              delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
 +              delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
  
        free(curr_branch);
        am_destroy(state);
diff --combined cache.h
+++ b/cache.h
@@@ -14,7 -14,6 +14,7 @@@
  #include "hash.h"
  #include "path.h"
  #include "sha1-array.h"
 +#include "repository.h"
  
  #ifndef platform_SHA_CTX
  /*
@@@ -78,8 -77,6 +78,8 @@@ struct object_id 
        unsigned char hash[GIT_MAX_RAWSZ];
  };
  
 +#define the_hash_algo the_repository->hash_algo
 +
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
@@@ -207,7 -204,6 +207,7 @@@ struct cache_entry 
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 +#define CE_FSMONITOR_VALID   (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -331,7 -327,6 +331,7 @@@ static inline unsigned int canon_mode(u
  #define CACHE_TREE_CHANGED    (1 << 5)
  #define SPLIT_INDEX_ORDERED   (1 << 6)
  #define UNTRACKED_CHANGED     (1 << 7)
 +#define FSMONITOR_CHANGED     (1 << 8)
  
  struct split_index;
  struct untracked_cache;
@@@ -350,8 -345,6 +350,8 @@@ struct index_state 
        struct hashmap dir_hash;
        unsigned char sha1[20];
        struct untracked_cache *untracked;
 +      uint64_t fsmonitor_last_update;
 +      struct ewah_bitmap *fsmonitor_dirty;
  };
  
  extern struct index_state the_index;
@@@ -454,16 -447,6 +454,16 @@@ static inline enum object_type object_t
  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
  /*
 + * Environment variable used in handshaking the wire protocol.
 + * Contains a colon ':' separated list of keys with optional values
 + * 'key[=value]'.  Presence of unknown keys and values must be
 + * ignored.
 + */
 +#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
 +/* HTTP header used to handshake the wire protocol */
 +#define GIT_PROTOCOL_HEADER "Git-Protocol"
 +
 +/*
   * This environment variable is expected to contain a boolean indicating
   * whether we should or should not treat:
   *
@@@ -619,31 -602,21 +619,40 @@@ extern int do_read_index(struct index_s
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 +
 +/* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 -#define CLOSE_LOCK            (1 << 1)
 +
 +/*
 + * Write the index while holding an already-taken lock. Close the lock,
 + * and if `COMMIT_LOCK` is given, commit it.
 + *
 + * Unless a split index is in use, write the index into the lockfile.
 + *
 + * With a split index, write the shared index to a temporary file,
 + * adjust its permissions and rename it into place, then write the
 + * split index to the lockfile. If the temporary file for the shared
 + * index cannot be created, fall back to the behavior described in
 + * the previous paragraph.
 + *
 + * With `COMMIT_LOCK`, the lock is always committed or rolled back.
 + * Without it, the lock is closed, but neither committed nor rolled
 + * back.
 + */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 +
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
+ /**
+  * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
+  * branch, returns 1 if there are entries in the index, 0 otherwise. If an
+  * strbuf is provided, the space-separated list of files that differ will be
+  * appended to it.
+  */
+ extern int index_has_changes(struct strbuf *sb);
  extern int verify_path(const char *path);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
@@@ -717,14 -690,11 +726,14 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_IGNORE_MISSING               0x08
  /* enable stat refresh */
  #define CE_MATCH_REFRESH              0x10
 -extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 -extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 +#define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
 +#define HASH_RENORMALIZE  4
  extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
  extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
@@@ -755,17 -725,12 +764,17 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
 +/*
 + * Opportunistically update the index but do not complain if we can't.
 + * The lockfile is always committed or rolled back.
 + */
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
 +extern int verify_ce_order;
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -819,7 -784,6 +828,7 @@@ extern int core_apply_sparse_checkout
  extern int precomposed_unicode;
  extern int protect_hfs;
  extern int protect_ntfs;
 +extern const char *core_fsmonitor;
  
  /*
   * Include broken refs in all ref iterations, which will
@@@ -910,7 -874,6 +919,7 @@@ struct repository_format 
        int version;
        int precious_objects;
        int is_bare;
 +      int hash_algo;
        char *work_tree;
        struct string_list unknown_extensions;
  };
@@@ -1043,22 -1006,22 +1052,22 @@@ extern const struct object_id empty_blo
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_tree);
  }
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1364,13 -1327,6 +1373,13 @@@ extern int get_sha1_hex(const char *hex
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
  /*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 +
 +/*
   * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
   * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
@@@ -1491,7 -1447,6 +1500,7 @@@ extern const char *ident_default_name(v
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
  extern void reset_ident_date(void);
  
@@@ -1973,10 -1928,4 +1982,10 @@@ void sleep_millisec(int millisec)
   */
  void safe_create_dir(const char *dir, int share);
  
 +/*
 + * Should we print an ellipsis after an abbreviated SHA-1 value
 + * when doing diff-raw output or indicating a detached HEAD?
 + */
 +extern int print_sha1_ellipsis(void);
 +
  #endif /* CACHE_H */
diff --combined merge-recursive.c
@@@ -540,8 -540,8 +540,8 @@@ static struct string_list *get_renames(
                return renames;
  
        diff_setup(&opts);
 -      DIFF_OPT_SET(&opts, RECURSIVE);
 -      DIFF_OPT_CLR(&opts, RENAME_EMPTY);
 +      opts.flags.recursive = 1;
 +      opts.flags.rename_empty = 0;
        opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
@@@ -646,7 -646,7 +646,7 @@@ static int remove_file(struct merge_opt
                if (ignore_case) {
                        struct cache_entry *ce;
                        ce = cache_file_exists(path, strlen(path), ignore_case);
 -                      if (ce && ce_stage(ce) == 0)
 +                      if (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))
                                return 0;
                }
                if (remove_path(path))
@@@ -1952,6 -1952,13 +1952,13 @@@ int merge_trees(struct merge_options *o
        }
  
        if (oid_eq(&common->object.oid, &merge->object.oid)) {
+               struct strbuf sb = STRBUF_INIT;
+               if (index_has_changes(&sb)) {
+                       err(o, _("Dirty index: cannot merge (dirty: %s)"),
+                           sb.buf);
+                       return 0;
+               }
                output(o, 0, _("Already up to date!"));
                *result = head;
                return 1;
@@@ -2082,7 -2089,7 +2089,7 @@@ int merge_recursive(struct merge_option
                /* if there is no common ancestor, use an empty tree */
                struct tree *tree;
  
 -              tree = lookup_tree(&empty_tree_oid);
 +              tree = lookup_tree(the_hash_algo->empty_tree);
                merged_common_ancestors = make_virtual_commit(tree, "ancestor");
        }
  
@@@ -2163,7 -2170,7 +2170,7 @@@ int merge_recursive_generic(struct merg
                            struct commit **result)
  {
        int clean;
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock = LOCK_INIT;
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
        struct commit_list *ca = NULL;
                }
        }
  
 -      hold_locked_index(lock, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (clean < 0)
                return clean;
  
        if (active_cache_changed &&
 -          write_locked_index(&the_index, lock, COMMIT_LOCK))
 +          write_locked_index(&the_index, &lock, COMMIT_LOCK))
                return err(o, _("Unable to write index."));
  
        return clean ? 0 : 1;
@@@ -2202,7 -2209,6 +2209,7 @@@ static void merge_recursive_config(stru
  
  void init_merge_options(struct merge_options *o)
  {
 +      const char *merge_verbosity;
        memset(o, 0, sizeof(struct merge_options));
        o->verbosity = 2;
        o->buffer_output = 1;
        o->renormalize = 0;
        o->detect_rename = 1;
        merge_recursive_config(o);
 -      if (getenv("GIT_MERGE_VERBOSITY"))
 -              o->verbosity =
 -                      strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
 +      merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
 +      if (merge_verbosity)
 +              o->verbosity = strtol(merge_verbosity, NULL, 10);
        if (o->verbosity >= 5)
                o->buffer_output = 0;
        strbuf_init(&o->obuf, 0);
@@@ -2253,8 -2259,6 +2260,8 @@@ int parse_merge_opt(struct merge_option
                DIFF_XDL_SET(o, IGNORE_WHITESPACE);
        else if (!strcmp(s, "ignore-space-at-eol"))
                DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);
 +      else if (!strcmp(s, "ignore-cr-at-eol"))
 +              DIFF_XDL_SET(o, IGNORE_CR_AT_EOL);
        else if (!strcmp(s, "renormalize"))
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
diff --combined merge.c
+++ b/merge.c
@@@ -1,4 -1,6 +1,6 @@@
  #include "cache.h"
+ #include "diff.h"
+ #include "diffcore.h"
  #include "lockfile.h"
  #include "commit.h"
  #include "run-command.h"
@@@ -15,6 -17,37 +17,37 @@@ static const char *merge_argument(struc
                return EMPTY_TREE_SHA1_HEX;
  }
  
 -              DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
+ int index_has_changes(struct strbuf *sb)
+ {
+       struct object_id head;
+       int i;
+       if (!get_oid_tree("HEAD", &head)) {
+               struct diff_options opt;
+               diff_setup(&opt);
 -                      DIFF_OPT_SET(&opt, QUICK);
++              opt.flags.exit_with_status = 1;
+               if (!sb)
 -              return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
++                      opt.flags.quick = 1;
+               do_diff_cache(&head, &opt);
+               diffcore_std(&opt);
+               for (i = 0; sb && i < diff_queued_diff.nr; i++) {
+                       if (i)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
+               }
+               diff_flush(&opt);
++              return opt.flags.has_changes != 0;
+       } else {
+               for (i = 0; sb && i < active_nr; i++) {
+                       if (i)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, active_cache[i]->name);
+               }
+               return !!active_nr;
+       }
+ }
  int try_merge_command(const char *strategy, size_t xopts_nr,
                      const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
@@@ -53,11 -86,11 +86,11 @@@ int checkout_fast_forward(const struct 
        struct tree_desc t[MAX_UNPACK_TREES];
        int i, nr_trees = 0;
        struct dir_struct dir;
 -      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
  
        refresh_cache(REFRESH_QUIET);
  
 -      if (hold_locked_index(lock_file, LOCK_REPORT_ON_ERROR) < 0)
 +      if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
  
        memset(&trees, 0, sizeof(trees));
        }
        if (unpack_trees(nr_trees, t, &opts))
                return -1;
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) {
 -              rollback_lock_file(lock_file);
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                return error(_("unable to write new index file"));
 -      }
        return 0;
  }