Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 builtin/reset.c       |  2 ++
 merge-recursive.c     |  2 +-
 t/t2080-backup-log.sh | 14 +++++++++
 unpack-trees.c        | 70 +++++++++++++++++++++++++++++++++----------
 unpack-trees.h        |  3 +-
 5 files changed, 74 insertions(+), 17 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 58166964f8..517a27dce5 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -67,6 +67,8 @@ static int reset_index(const struct object_id *oid, int 
reset_type, int quiet)
                break;
        case HARD:
                opts.update = 1;
+               repo_config_get_bool(the_repository, "core.backupLog",
+                                    &opts.keep_backup);
                /* fallthrough */
        default:
                opts.reset = 1;
diff --git a/merge-recursive.c b/merge-recursive.c
index acc2f64a4e..10a9d3180a 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -896,7 +896,7 @@ static int was_dirty(struct merge_options *o, const char 
*path)
 
        ce = index_file_exists(o->unpack_opts.src_index,
                               path, strlen(path), ignore_case);
-       dirty = verify_uptodate(ce, &o->unpack_opts) != 0;
+       dirty = verify_uptodate(ce, &o->unpack_opts, NULL) != 0;
        return dirty;
 }
 
diff --git a/t/t2080-backup-log.sh b/t/t2080-backup-log.sh
index a283528912..901755ce93 100755
--- a/t/t2080-backup-log.sh
+++ b/t/t2080-backup-log.sh
@@ -211,4 +211,18 @@ test_expect_success 'overwritten ignored file is backed 
up' '
        )
 '
 
+test_expect_success 'overwritten out-of-date file is backed up' '
+       git init overwrite-outofdate &&
+       (
+               cd overwrite-outofdate &&
+               test_commit haha &&
+               NEW=$(git hash-object haha.t) &&
+               echo bad >>haha.t &&
+               OLD=$(git hash-object haha.t) &&
+               git -c core.backupLog reset --hard &&
+               echo "$OLD $NEW $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 
$test_tick -0700     haha.t" >expected &&
+               test_cmp expected .git/worktree.bkl
+       )
+'
+
 test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index 8d7273af2b..221869b47c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1715,7 +1715,8 @@ static int same(const struct cache_entry *a, const struct 
cache_entry *b)
  */
 static int verify_uptodate_1(const struct cache_entry *ce,
                             struct unpack_trees_options *o,
-                            enum unpack_trees_error_types error_type)
+                            enum unpack_trees_error_types error_type,
+                            struct object_id *old_hash)
 {
        struct stat st;
 
@@ -1727,10 +1728,16 @@ static int verify_uptodate_1(const struct cache_entry 
*ce,
         * if this entry is truly up-to-date because this file may be
         * overwritten.
         */
-       if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
+       if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
                ; /* keep checking */
-       else if (o->reset || ce_uptodate(ce))
+       } else if (o->reset) {
+               if (o->keep_backup && old_hash && !lstat(ce->name, &st))
+                       index_path(NULL, old_hash, ce->name, &st,
+                                  HASH_WRITE_OBJECT);
+               return 0;
+       } else if (ce_uptodate(ce)) {
                return 0;
+       }
 
        if (!lstat(ce->name, &st)) {
                int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
@@ -1764,17 +1771,20 @@ static int verify_uptodate_1(const struct cache_entry 
*ce,
 }
 
 int verify_uptodate(const struct cache_entry *ce,
-                   struct unpack_trees_options *o)
+                   struct unpack_trees_options *o,
+                   struct object_id *old_hash)
 {
+       if (o->keep_backup && old_hash)
+               oidclr(old_hash);
        if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
                return 0;
-       return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
+       return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE, old_hash);
 }
 
 static int verify_uptodate_sparse(const struct cache_entry *ce,
                                  struct unpack_trees_options *o)
 {
-       return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
+       return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE, NULL);
 }
 
 /*
@@ -1862,8 +1872,11 @@ static int verify_clean_subdirectory(const struct 
cache_entry *ce,
                 * removed.
                 */
                if (!ce_stage(ce2)) {
-                       if (verify_uptodate(ce2, o))
+                       struct object_id old_hash;
+
+                       if (verify_uptodate(ce2, o, &old_hash))
                                return -1;
+                       make_backup(ce2, &old_hash, NULL, o);
                        add_entry(o, ce2, CE_REMOVE, 0);
                        invalidate_ce_path(ce, o);
                        mark_ce_used(ce2, o);
@@ -1973,8 +1986,13 @@ static int verify_absent_1(const struct cache_entry *ce,
        int len;
        struct stat st;
 
-       if (o->index_only || o->reset || !o->update)
+       if (o->index_only || o->reset || !o->update) {
+               if (o->reset && o->keep_backup &&
+                   old_hash && !lstat(ce->name, &st))
+                       index_path(NULL, old_hash, ce->name, &st,
+                                  HASH_WRITE_OBJECT);
                return 0;
+       }
 
        len = check_leading_path(ce->name, ce_namelen(ce));
        if (!len)
@@ -2092,10 +2110,12 @@ static int merged_entry(const struct cache_entry *ce,
                        copy_cache_entry(merge, old);
                        update = 0;
                } else {
-                       if (verify_uptodate(old, o)) {
+                       struct object_id old_hash;
+                       if (verify_uptodate(old, o, &old_hash)) {
                                discard_cache_entry(merge);
                                return -1;
                        }
+                       make_backup(old, &old_hash, &merge->oid, o);
                        /* Migrate old flags over */
                        update |= old->ce_flags & (CE_SKIP_WORKTREE | 
CE_NEW_SKIP_WORKTREE);
                        invalidate_ce_path(old, o);
@@ -2124,18 +2144,20 @@ static int deleted_entry(const struct cache_entry *ce,
                         const struct cache_entry *old,
                         struct unpack_trees_options *o)
 {
+       struct object_id old_hash;
+
        /* Did it exist in the index? */
        if (!old) {
-               struct object_id old_hash;
-
                if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
                                  o, &old_hash))
                        return -1;
                make_backup(ce, &old_hash, NULL, o);
                return 0;
        }
-       if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
+       if (!(old->ce_flags & CE_CONFLICTED) &&
+           verify_uptodate(old, o, &old_hash))
                return -1;
+       make_backup(ce, &old_hash, NULL, o);
        add_entry(o, ce, CE_REMOVE, 0);
        invalidate_ce_path(ce, o);
        return 1;
@@ -2305,8 +2327,16 @@ int threeway_merge(const struct cache_entry * const 
*stages,
         * conflict resolution files.
         */
        if (index) {
-               if (verify_uptodate(index, o))
+               struct object_id old_hash;
+
+               if (verify_uptodate(index, o, &old_hash))
                        return -1;
+               /*
+                * A new conflict appears. We could make a backup from
+                * worktree version to stage 2 or 3. But neither makes much
+                * sense. Make a deletion backup instead.
+                */
+               make_backup(index, &old_hash, NULL, o);
        }
 
        o->nontrivial_merge = 1;
@@ -2447,16 +2477,26 @@ int oneway_merge(const struct cache_entry * const *src,
                return deleted_entry(old, old, o);
 
        if (old && same(old, a)) {
+               struct object_id old_hash;
                int update = 0;
+
+               oidclr(&old_hash);
                if (o->reset && o->update && !ce_uptodate(old) && 
!ce_skip_worktree(old)) {
                        struct stat st;
+
                        if (lstat(old->name, &st) ||
-                           ie_match_stat(o->src_index, old, &st, 
CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
+                           ie_match_stat(o->src_index, old, &st,
+                                         
CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
                                update |= CE_UPDATE;
+
+                       if (update & CE_UPDATE && o->keep_backup)
+                               index_path(NULL, &old_hash, old->name, &st,
+                                          HASH_WRITE_OBJECT);
                }
                if (o->update && S_ISGITLINK(old->ce_mode) &&
-                   should_update_submodules() && !verify_uptodate(old, o))
+                   should_update_submodules() && !verify_uptodate(old, o, 
NULL))
                        update |= CE_UPDATE;
+               make_backup(old, &old_hash, &old->oid, o);
                add_entry(o, old, update, 0);
                return 0;
        }
diff --git a/unpack-trees.h b/unpack-trees.h
index e2a64e2401..a453def564 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -93,7 +93,8 @@ int unpack_trees(unsigned n, struct tree_desc *t,
                 struct unpack_trees_options *options);
 
 int verify_uptodate(const struct cache_entry *ce,
-                   struct unpack_trees_options *o);
+                   struct unpack_trees_options *o,
+                   struct object_id *old_hash);
 
 int threeway_merge(const struct cache_entry * const *stages,
                   struct unpack_trees_options *o);
-- 
2.20.0.rc2.486.g9832c05c3d

Reply via email to