rerere: gc and clear
Adjust "git rerere gc" and "git rerere clear" to the new world order with rerere database with multiple variants for the same shape of conflicts. Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano committed
Mar 8, 2016 at 12:11 UTC
1be1e85115244a3a11f89b75c798823a7dce795f
2 files changed
+123
-45
rerere.c
+48
-39
@@ -1081,29 +1081,16 @@ int rerere_forget(struct pathspec *pathspec)
1081
* Garbage collection support
1082
*/
1083
1084
-/*
1085
- * Note that this is not reentrant but is used only one-at-a-time
1086
- * so it does not matter right now.
1087
- */
1088
-static struct rerere_id *dirname_to_id(const char *name)
1089
-{
1090
- static struct rerere_id id;
1091
- id.collection = find_rerere_dir(name);
1092
- return &id;
1093
-}
1094
-
1095
-static time_t rerere_created_at(const char *dir_name)
1084
+static time_t rerere_created_at(struct rerere_id *id)
1085
{
1086
struct stat st;
1098
- struct rerere_id *id = dirname_to_id(dir_name);
1087
1088
return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
1089
}
1090
1103
-static time_t rerere_last_used_at(const char *dir_name)
1091
+static time_t rerere_last_used_at(struct rerere_id *id)
1092
{
1093
struct stat st;
1106
- struct rerere_id *id = dirname_to_id(dir_name);
1094
1095
return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
1096
}
@@ -1113,15 +1100,28 @@ static time_t rerere_last_used_at(const char *dir_name)
1100
*/
1101
static void unlink_rr_item(struct rerere_id *id)
1102
{
1116
- unlink(rerere_path(id, "thisimage"));
1117
- unlink(rerere_path(id, "preimage"));
1118
- unlink(rerere_path(id, "postimage"));
1119
- /*
1120
- * NEEDSWORK: what if this rmdir() fails? Wouldn't we then
1121
- * assume that we already have preimage recorded in
1122
- * do_plain_rerere()?
1123
- */
1124
- rmdir(rerere_path(id, NULL));
1103
+ unlink_or_warn(rerere_path(id, "thisimage"));
1104
+ remove_variant(id);
1105
+ id->collection->status[id->variant] = 0;
1106
+}
1107
+
1108
+static void prune_one(struct rerere_id *id, time_t now,
1109
+ int cutoff_resolve, int cutoff_noresolve)
1110
+{
1111
+ time_t then;
1112
+ int cutoff;
1113
+
1114
+ then = rerere_last_used_at(id);
1115
+ if (then)
1116
+ cutoff = cutoff_resolve;
1117
+ else {
1118
+ then = rerere_created_at(id);
1119
+ if (!then)
1120
+ return;
1121
+ cutoff = cutoff_noresolve;
1122
+ }
1123
+ if (then < now - cutoff * 86400)
1124
+ unlink_rr_item(id);
1125
}
1126
1127
void rerere_gc(struct string_list *rr)
@@ -1129,8 +1129,8 @@ void rerere_gc(struct string_list *rr)
1129
struct string_list to_remove = STRING_LIST_INIT_DUP;
1130
DIR *dir;
1131
struct dirent *e;
1132
- int i, cutoff;
1133
- time_t now = time(NULL), then;
1132
+ int i;
1133
+ time_t now = time(NULL);
1134
int cutoff_noresolve = 15;
1135
int cutoff_resolve = 60;
1136
@@ -1142,25 +1142,32 @@ void rerere_gc(struct string_list *rr)
1142
die_errno("unable to open rr-cache directory");
1143
/* Collect stale conflict IDs ... */
1144
while ((e = readdir(dir))) {
1145
+ struct rerere_dir *rr_dir;
1146
+ struct rerere_id id;
1147
+ int now_empty;
1148
+
1149
if (is_dot_or_dotdot(e->d_name))
1150
continue;
1147
-
1148
- then = rerere_last_used_at(e->d_name);
1149
- if (then) {
1150
- cutoff = cutoff_resolve;
1151
- } else {
1152
- then = rerere_created_at(e->d_name);
1153
- if (!then)
1154
- continue;
1155
- cutoff = cutoff_noresolve;
1151
+ rr_dir = find_rerere_dir(e->d_name);
1152
+ if (!rr_dir)
1153
+ continue; /* or should we remove e->d_name? */
1154
+
1155
+ now_empty = 1;
1156
+ for (id.variant = 0, id.collection = rr_dir;
1157
+ id.variant < id.collection->status_nr;
1158
+ id.variant++) {
1159
+ prune_one(&id, now, cutoff_resolve, cutoff_noresolve);
1160
+ if (id.collection->status[id.variant])
1161
+ now_empty = 0;
1162
}
1157
- if (then < now - cutoff * 86400)
1163
+ if (now_empty)
1164
string_list_append(&to_remove, e->d_name);
1165
}
1166
closedir(dir);
1161
- /* ... and then remove them one-by-one */
1167
+
1168
+ /* ... and then remove the empty directories */
1169
for (i = 0; i < to_remove.nr; i++)
1163
- unlink_rr_item(dirname_to_id(to_remove.items[i].string));
1170
+ rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
1171
string_list_clear(&to_remove, 0);
1172
}
1173
@@ -1177,8 +1184,10 @@ void rerere_clear(struct string_list *merge_rr)
1184
1185
for (i = 0; i < merge_rr->nr; i++) {
1186
struct rerere_id *id = merge_rr->items[i].util;
1180
- if (!has_rerere_resolution(id))
1187
+ if (!has_rerere_resolution(id)) {
1188
unlink_rr_item(id);
1189
+ rmdir(rerere_path(id, NULL));
1190
+ }
1191
}
1192
unlink_or_warn(git_path("MERGE_RR"));
1193
}
t/t4200-rerere.sh
+75
-6
@@ -412,6 +412,39 @@ concat_insert () {
412
cat early && printf "%s\n" "$@" && cat late "$last"
413
}
414
415
+count_pre_post () {
416
+ find .git/rr-cache/ -type f -name "preimage*" >actual &&
417
+ test_line_count = "$1" actual &&
418
+ find .git/rr-cache/ -type f -name "postimage*" >actual &&
419
+ test_line_count = "$2" actual
420
+}
421
+
422
+test_expect_success 'rerere gc' '
423
+ find .git/rr-cache -type f >original &&
424
+ xargs test-chmtime -172800 <original &&
425
+
426
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
427
+ find .git/rr-cache -type f >actual &&
428
+ test_cmp original actual &&
429
+
430
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=0 rerere gc &&
431
+ find .git/rr-cache -type f >actual &&
432
+ test_cmp original actual &&
433
+
434
+ git -c gc.rerereresolved=0 -c gc.rerereunresolved=0 rerere gc &&
435
+ find .git/rr-cache -type f >actual &&
436
+ >expect &&
437
+ test_cmp expect actual
438
+'
439
+
440
+merge_conflict_resolve () {
441
+ git reset --hard &&
442
+ test_must_fail git merge six.1 &&
443
+ # Resolution is to replace 7 with 6.1 and 6.2 (i.e. take both)
444
+ concat_insert short 6.1 6.2 >file1 &&
445
+ concat_insert long 6.1 6.2 >file2
446
+}
447
+
448
test_expect_success 'multiple identical conflicts' '
449
git reset --hard &&
450
@@ -441,7 +474,7 @@ test_expect_success 'multiple identical conflicts' '
474
# - six.1 replaces these 7s with 6.1
475
# - six.2 replaces these 7s with 6.2
476
444
- test_must_fail git merge six.1 &&
477
+ merge_conflict_resolve &&
478
479
# Check that rerere knows that file1 and file2 have conflicts
480
@@ -452,19 +485,43 @@ test_expect_success 'multiple identical conflicts' '
485
git rerere status | sort >actual &&
486
test_cmp expect actual &&
487
455
- # Resolution is to replace 7 with 6.1 and 6.2 (i.e. take both)
456
- concat_insert short 6.1 6.2 >file1 &&
457
- concat_insert long 6.1 6.2 >file2 &&
458
-
488
git rerere remaining >actual &&
489
test_cmp expect actual &&
490
491
+ count_pre_post 2 0 &&
492
+
493
+ # Pretend that the conflicts were made quite some time ago
494
+ find .git/rr-cache/ -type f | xargs test-chmtime -172800 &&
495
+
496
+ # Unresolved entries have not expired yet
497
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
498
+ count_pre_post 2 0 &&
499
+
500
+ # Unresolved entries have expired
501
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=1 rerere gc &&
502
+ count_pre_post 0 0 &&
503
+
504
+ # Recreate the conflicted state
505
+ merge_conflict_resolve &&
506
+ count_pre_post 2 0 &&
507
+
508
+ # Clear it
509
+ git rerere clear &&
510
+ count_pre_post 0 0 &&
511
+
512
+ # Recreate the conflicted state
513
+ merge_conflict_resolve &&
514
+ count_pre_post 2 0 &&
515
+
516
# We resolved file1 and file2
517
git rerere &&
518
>expect &&
519
git rerere remaining >actual &&
520
test_cmp expect actual &&
521
522
+ # We must have recorded both of them
523
+ count_pre_post 2 2 &&
524
+
525
# Now we should be able to resolve them both
526
git reset --hard &&
527
test_must_fail git merge six.1 &&
@@ -477,7 +534,19 @@ test_expect_success 'multiple identical conflicts' '
534
concat_insert short 6.1 6.2 >file1.expect &&
535
concat_insert long 6.1 6.2 >file2.expect &&
536
test_cmp file1.expect file1 &&
480
- test_cmp file2.expect file2
537
+ test_cmp file2.expect file2 &&
538
+
539
+ # Pretend that the resolutions are old again
540
+ find .git/rr-cache/ -type f | xargs test-chmtime -172800 &&
541
+
542
+ # Resolved entries have not expired yet
543
+ git -c gc.rerereresolved=5 -c gc.rerereunresolved=5 rerere gc &&
544
+
545
+ count_pre_post 2 2 &&
546
+
547
+ # Resolved entries have expired
548
+ git -c gc.rerereresolved=1 -c gc.rerereunresolved=5 rerere gc &&
549
+ count_pre_post 0 0
550
'
551
552
test_done