Merge branch 'en/rerere-multi-stage-1-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 21 Nov 2018 13:57:44 +0000 (22:57 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 21 Nov 2018 13:57:44 +0000 (22:57 +0900)
A corner case bugfix in "git rerere" code.

* en/rerere-multi-stage-1-fix:
rerere: avoid buffer overrun
t4200: demonstrate rerere segfault on specially crafted merge

1  2 
rerere.c
t/t4200-rerere.sh
diff --combined rerere.c
index c7787aa07f80f00589e3c4f4e4e3cc825c43044e,ee02dd32446eacfd3bf93dc68ad7e35416e63098..783d4dae2aa2d7a17c37dfac892e1acdeb4b1db8
+++ b/rerere.c
@@@ -9,7 -9,6 +9,7 @@@
  #include "ll-merge.h"
  #include "attr.h"
  #include "pathspec.h"
 +#include "object-store.h"
  #include "sha1-lookup.h"
  
  #define RESOLVED 0
@@@ -201,7 -200,7 +201,7 @@@ static struct rerere_id *new_rerere_id(
  static void read_rr(struct string_list *rr)
  {
        struct strbuf buf = STRBUF_INIT;
 -      FILE *in = fopen_or_warn(git_path_merge_rr(), "r");
 +      FILE *in = fopen_or_warn(git_path_merge_rr(the_repository), "r");
  
        if (!in)
                return;
@@@ -533,7 -532,7 +533,7 @@@ static int check_one_conflict(int i, in
        }
  
        *type = PUNTED;
-       while (ce_stage(active_cache[i]) == 1)
+       while (i < active_nr && ce_stage(active_cache[i]) == 1)
                i++;
  
        /* Only handle regular files with both stages #2 and #3 */
@@@ -704,9 -703,10 +704,9 @@@ out
        return ret;
  }
  
 -static struct lock_file index_lock;
 -
  static void update_paths(struct string_list *update)
  {
 +      struct lock_file index_lock = LOCK_INIT;
        int i;
  
        hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
@@@ -896,8 -896,7 +896,8 @@@ int setup_rerere(struct string_list *me
        if (flags & RERERE_READONLY)
                fd = 0;
        else
 -              fd = hold_lock_file_for_update(&write_lock, git_path_merge_rr(),
 +              fd = hold_lock_file_for_update(&write_lock,
 +                                             git_path_merge_rr(the_repository),
                                               LOCK_DIE_ON_ERROR);
        read_rr(merge_rr);
        return fd;
@@@ -980,8 -979,8 +980,8 @@@ static int handle_cache(const char *pat
                        break;
                i = ce_stage(ce) - 1;
                if (!mmfile[i].ptr) {
 -                      mmfile[i].ptr = read_sha1_file(ce->oid.hash, &type,
 -                                                     &size);
 +                      mmfile[i].ptr = read_object_file(&ce->oid, &type,
 +                                                       &size);
                        mmfile[i].size = size;
                }
        }
@@@ -1120,7 -1119,7 +1120,7 @@@ int rerere_forget(struct pathspec *path
        find_conflict(&conflict);
        for (i = 0; i < conflict.nr; i++) {
                struct string_list_item *it = &conflict.items[i];
 -              if (!match_pathspec(pathspec, it->string,
 +              if (!match_pathspec(&the_index, pathspec, it->string,
                                    strlen(it->string), 0, NULL, 0))
                        continue;
                rerere_forget_one_path(it->string, &merge_rr);
@@@ -1247,6 -1246,6 +1247,6 @@@ void rerere_clear(struct string_list *m
                        rmdir(rerere_path(id, NULL));
                }
        }
 -      unlink_or_warn(git_path_merge_rr());
 +      unlink_or_warn(git_path_merge_rr(the_repository));
        rollback_lock_file(&write_lock);
  }
diff --combined t/t4200-rerere.sh
index 65da74c76683c0ccd109fdec63766ab6bdfb08f0,8da998f661e55c849599c6a0dcb02e43a327f413..313222d0d6251f33734b12758745999a79fe6bf2
@@@ -166,7 -166,7 +166,7 @@@ test_expect_success 'first postimage wi
        git commit -q -a -m "prefer first over second" &&
        test -f $rr/postimage &&
  
 -      oldmtimepost=$(test-chmtime -v -60 $rr/postimage | cut -f 1) &&
 +      oldmtimepost=$(test-tool chmtime --get -60 $rr/postimage) &&
  
        git checkout -b third master &&
        git show second^:a1 | sed "s/To die: t/To die! T/" >a1 &&
  '
  
  test_expect_success 'rerere updates postimage timestamp' '
 -      newmtimepost=$(test-chmtime -v +0 $rr/postimage | cut -f 1) &&
 +      newmtimepost=$(test-tool chmtime --get $rr/postimage) &&
        test $oldmtimepost -lt $newmtimepost
  '
  
@@@ -220,9 -220,9 +220,9 @@@ test_expect_success 'set up for garbag
        almost_60_days_ago=$((60-60*86400)) &&
        just_over_60_days_ago=$((-1-60*86400)) &&
  
 -      test-chmtime =$just_over_60_days_ago $rr/preimage &&
 -      test-chmtime =$almost_60_days_ago $rr/postimage &&
 -      test-chmtime =$almost_15_days_ago $rr2/preimage
 +      test-tool chmtime =$just_over_60_days_ago $rr/preimage &&
 +      test-tool chmtime =$almost_60_days_ago $rr/postimage &&
 +      test-tool chmtime =$almost_15_days_ago $rr2/preimage
  '
  
  test_expect_success 'gc preserves young or recently used records' '
  '
  
  test_expect_success 'old records rest in peace' '
 -      test-chmtime =$just_over_60_days_ago $rr/postimage &&
 -      test-chmtime =$just_over_15_days_ago $rr2/preimage &&
 +      test-tool chmtime =$just_over_60_days_ago $rr/postimage &&
 +      test-tool chmtime =$just_over_15_days_ago $rr2/preimage &&
        git rerere gc &&
        ! test -f $rr/preimage &&
        ! test -f $rr2/preimage
@@@ -243,14 -243,14 +243,14 @@@ rerere_gc_custom_expiry_test () 
        five_days="$1" right_now="$2"
        test_expect_success "rerere gc with custom expiry ($five_days, $right_now)" '
                rm -fr .git/rr-cache &&
 -              rr=.git/rr-cache/$_z40 &&
 +              rr=.git/rr-cache/$ZERO_OID &&
                mkdir -p "$rr" &&
                >"$rr/preimage" &&
                >"$rr/postimage" &&
  
                two_days_ago=$((-2*86400)) &&
 -              test-chmtime =$two_days_ago "$rr/preimage" &&
 -              test-chmtime =$two_days_ago "$rr/postimage" &&
 +              test-tool chmtime =$two_days_ago "$rr/preimage" &&
 +              test-tool chmtime =$two_days_ago "$rr/postimage" &&
  
                find .git/rr-cache -type f | sort >original &&
  
                git -c "gc.rerereresolved=$right_now" \
                    -c "gc.rerereunresolved=$right_now" rerere gc &&
                find .git/rr-cache -type f | sort >actual &&
 -              >expect &&
 -              test_cmp expect actual
 +              test_must_be_empty actual
        '
  }
  
@@@ -511,7 -512,7 +511,7 @@@ test_expect_success 'multiple identica
        count_pre_post 2 0 &&
  
        # Pretend that the conflicts were made quite some time ago
 -      find .git/rr-cache/ -type f | xargs test-chmtime -172800 &&
 +      test-tool chmtime -172800 $(find .git/rr-cache/ -type f) &&
  
        # Unresolved entries have not expired yet
        git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
  
        # We resolved file1 and file2
        git rerere &&
 -      >expect &&
        git rerere remaining >actual &&
 -      test_cmp expect actual &&
 +      test_must_be_empty actual &&
  
        # We must have recorded both of them
        count_pre_post 2 2 &&
        test_must_fail git merge six.1 &&
        git rerere &&
  
 -      >expect &&
        git rerere remaining >actual &&
 -      test_cmp expect actual &&
 +      test_must_be_empty actual &&
  
        concat_insert short 6.1 6.2 >file1.expect &&
        concat_insert long 6.1 6.2 >file2.expect &&
        git rerere &&
  
        # Pretend that the resolutions are old again
 -      find .git/rr-cache/ -type f | xargs test-chmtime -172800 &&
 +      test-tool chmtime -172800 $(find .git/rr-cache/ -type f) &&
  
        # Resolved entries have not expired yet
        git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
        count_pre_post 0 0
  '
  
+ test_expect_success 'setup simple stage 1 handling' '
+       test_create_repo stage_1_handling &&
+       (
+               cd stage_1_handling &&
+               test_seq 1 10 >original &&
+               git add original &&
+               git commit -m original &&
+               git checkout -b A master &&
+               git mv original A &&
+               git commit -m "rename to A" &&
+               git checkout -b B master &&
+               git mv original B &&
+               git commit -m "rename to B"
+       )
+ '
+ test_expect_success 'test simple stage 1 handling' '
+       (
+               cd stage_1_handling &&
+               git config rerere.enabled true &&
+               git checkout A^0 &&
+               test_must_fail git merge B^0
+       )
+ '
  test_done