Git fork

Merge branch 'jk/diff-no-index-with-pathspec-fix' into maint-2.51

An earlier addition to "git diff --no-index A B" to limit the
output with pathspec after the two directories misbehaved when
these directories were given with a trailing slash, which has been
corrected.

* jk/diff-no-index-with-pathspec-fix:
diff --no-index: fix logic for paths ending in '/'

+51 -28
+35 -28
diff-no-index.c
··· 21 22 static int read_directory_contents(const char *path, struct string_list *list, 23 const struct pathspec *pathspec, 24 - int skip) 25 { 26 - struct strbuf match = STRBUF_INIT; 27 - int len; 28 DIR *dir; 29 struct dirent *e; 30 31 if (!(dir = opendir(path))) 32 return error("Could not open directory %s", path); 33 34 - if (pathspec) { 35 - strbuf_addstr(&match, path); 36 - strbuf_complete(&match, '/'); 37 - strbuf_remove(&match, 0, skip); 38 - 39 - len = match.len; 40 - } 41 - 42 while ((e = readdir_skip_dot_and_dotdot(dir))) { 43 if (pathspec) { 44 int is_dir = 0; 45 46 - strbuf_setlen(&match, len); 47 - strbuf_addstr(&match, e->d_name); 48 if (NOT_CONSTANT(DTYPE(e)) != DT_UNKNOWN) { 49 is_dir = (DTYPE(e) == DT_DIR); 50 } else { ··· 57 } 58 59 if (!match_leading_pathspec(NULL, pathspec, 60 - match.buf, match.len, 61 0, NULL, is_dir)) 62 continue; 63 } ··· 65 string_list_insert(list, e->d_name); 66 } 67 68 - strbuf_release(&match); 69 closedir(dir); 70 return 0; 71 } ··· 169 170 static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop, 171 const char *name1, const char *name2, int recursing, 172 - const struct pathspec *ps, int skip1, int skip2) 173 { 174 int mode1 = 0, mode2 = 0; 175 enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE; ··· 208 struct string_list p2 = STRING_LIST_INIT_DUP; 209 int i1, i2, ret = 0; 210 size_t len1 = 0, len2 = 0; 211 212 - if (name1 && read_directory_contents(name1, &p1, ps, skip1)) 213 return -1; 214 - if (name2 && read_directory_contents(name2, &p2, ps, skip2)) { 215 string_list_clear(&p1, 0); 216 return -1; 217 } ··· 235 strbuf_setlen(&buffer1, len1); 236 strbuf_setlen(&buffer2, len2); 237 238 if (i1 == p1.nr) 239 comp = 1; 240 else if (i2 == p2.nr) ··· 245 if (comp > 0) 246 n1 = NULL; 247 else { 248 - strbuf_addstr(&buffer1, p1.items[i1++].string); 249 n1 = buffer1.buf; 250 } 251 252 if (comp < 0) 253 n2 = NULL; 254 else { 255 - strbuf_addstr(&buffer2, p2.items[i2++].string); 256 n2 = buffer2.buf; 257 } 258 259 - ret = queue_diff(o, algop, n1, n2, 1, ps, skip1, skip2); 260 } 261 string_list_clear(&p1, 0); 262 string_list_clear(&p2, 0); ··· 346 int implicit_no_index, int argc, const char **argv) 347 { 348 struct pathspec pathspec, *ps = NULL; 349 - int i, no_index, skip1 = 0, skip2 = 0; 350 int ret = 1; 351 const char *paths[2]; 352 char *to_free[ARRAY_SIZE(paths)] = { 0 }; ··· 387 NULL, &argv[2]); 388 if (pathspec.nr) 389 ps = &pathspec; 390 - 391 - skip1 = strlen(paths[0]); 392 - skip1 += paths[0][skip1] == '/' ? 0 : 1; 393 - skip2 = strlen(paths[1]); 394 - skip2 += paths[1][skip2] == '/' ? 0 : 1; 395 } else if (argc > 2) { 396 warning(_("Limiting comparison with pathspecs is only " 397 "supported if both paths are directories.")); ··· 415 revs->diffopt.flags.exit_with_status = 1; 416 417 if (queue_diff(&revs->diffopt, algop, paths[0], paths[1], 0, ps, 418 - skip1, skip2)) 419 goto out; 420 diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); 421 diffcore_std(&revs->diffopt); ··· 431 for (i = 0; i < ARRAY_SIZE(to_free); i++) 432 free(to_free[i]); 433 strbuf_release(&replacement); 434 if (ps) 435 clear_pathspec(ps); 436 return ret;
··· 21 22 static int read_directory_contents(const char *path, struct string_list *list, 23 const struct pathspec *pathspec, 24 + struct strbuf *match) 25 { 26 + int len = match->len; 27 DIR *dir; 28 struct dirent *e; 29 30 if (!(dir = opendir(path))) 31 return error("Could not open directory %s", path); 32 33 while ((e = readdir_skip_dot_and_dotdot(dir))) { 34 if (pathspec) { 35 int is_dir = 0; 36 37 + strbuf_setlen(match, len); 38 + strbuf_addstr(match, e->d_name); 39 if (NOT_CONSTANT(DTYPE(e)) != DT_UNKNOWN) { 40 is_dir = (DTYPE(e) == DT_DIR); 41 } else { ··· 48 } 49 50 if (!match_leading_pathspec(NULL, pathspec, 51 + match->buf, match->len, 52 0, NULL, is_dir)) 53 continue; 54 } ··· 56 string_list_insert(list, e->d_name); 57 } 58 59 + strbuf_setlen(match, len); 60 closedir(dir); 61 return 0; 62 } ··· 160 161 static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop, 162 const char *name1, const char *name2, int recursing, 163 + const struct pathspec *ps, 164 + struct strbuf *ps_match1, struct strbuf *ps_match2) 165 { 166 int mode1 = 0, mode2 = 0; 167 enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE; ··· 200 struct string_list p2 = STRING_LIST_INIT_DUP; 201 int i1, i2, ret = 0; 202 size_t len1 = 0, len2 = 0; 203 + size_t match1_len = ps_match1->len; 204 + size_t match2_len = ps_match2->len; 205 206 + if (name1 && read_directory_contents(name1, &p1, ps, ps_match1)) 207 return -1; 208 + if (name2 && read_directory_contents(name2, &p2, ps, ps_match2)) { 209 string_list_clear(&p1, 0); 210 return -1; 211 } ··· 229 strbuf_setlen(&buffer1, len1); 230 strbuf_setlen(&buffer2, len2); 231 232 + if (ps) { 233 + strbuf_setlen(ps_match1, match1_len); 234 + strbuf_setlen(ps_match2, match2_len); 235 + } 236 + 237 if (i1 == p1.nr) 238 comp = 1; 239 else if (i2 == p2.nr) ··· 244 if (comp > 0) 245 n1 = NULL; 246 else { 247 + strbuf_addstr(&buffer1, p1.items[i1].string); 248 + if (ps) { 249 + strbuf_addstr(ps_match1, p1.items[i1].string); 250 + strbuf_complete(ps_match1, '/'); 251 + } 252 n1 = buffer1.buf; 253 + i1++; 254 } 255 256 if (comp < 0) 257 n2 = NULL; 258 else { 259 + strbuf_addstr(&buffer2, p2.items[i2].string); 260 + if (ps) { 261 + strbuf_addstr(ps_match2, p2.items[i2].string); 262 + strbuf_complete(ps_match2, '/'); 263 + } 264 n2 = buffer2.buf; 265 + i2++; 266 } 267 268 + ret = queue_diff(o, algop, n1, n2, 1, ps, ps_match1, ps_match2); 269 } 270 string_list_clear(&p1, 0); 271 string_list_clear(&p2, 0); ··· 355 int implicit_no_index, int argc, const char **argv) 356 { 357 struct pathspec pathspec, *ps = NULL; 358 + struct strbuf ps_match1 = STRBUF_INIT, ps_match2 = STRBUF_INIT; 359 + int i, no_index; 360 int ret = 1; 361 const char *paths[2]; 362 char *to_free[ARRAY_SIZE(paths)] = { 0 }; ··· 397 NULL, &argv[2]); 398 if (pathspec.nr) 399 ps = &pathspec; 400 } else if (argc > 2) { 401 warning(_("Limiting comparison with pathspecs is only " 402 "supported if both paths are directories.")); ··· 420 revs->diffopt.flags.exit_with_status = 1; 421 422 if (queue_diff(&revs->diffopt, algop, paths[0], paths[1], 0, ps, 423 + &ps_match1, &ps_match2)) 424 goto out; 425 diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); 426 diffcore_std(&revs->diffopt); ··· 436 for (i = 0; i < ARRAY_SIZE(to_free); i++) 437 free(to_free[i]); 438 strbuf_release(&replacement); 439 + strbuf_release(&ps_match1); 440 + strbuf_release(&ps_match2); 441 if (ps) 442 clear_pathspec(ps); 443 return ret;
+16
t/t4053-diff-no-index.sh
··· 339 test_cmp expect actual 340 ' 341 342 test_expect_success 'diff --no-index with pathspec no matches' ' 343 test_expect_code 0 git diff --name-status --no-index a b missing 344 '
··· 339 test_cmp expect actual 340 ' 341 342 + test_expect_success 'diff --no-index first path ending in slash with pathspec' ' 343 + test_expect_code 1 git diff --name-status --no-index a/ b 1 >actual && 344 + cat >expect <<-EOF && 345 + D a/1 346 + EOF 347 + test_cmp expect actual 348 + ' 349 + 350 + test_expect_success 'diff --no-index second path ending in slash with pathspec' ' 351 + test_expect_code 1 git diff --name-status --no-index a b/ 1 >actual && 352 + cat >expect <<-EOF && 353 + D a/1 354 + EOF 355 + test_cmp expect actual 356 + ' 357 + 358 test_expect_success 'diff --no-index with pathspec no matches' ' 359 test_expect_code 0 git diff --name-status --no-index a b missing 360 '