git-clean: implement partial matching for selection

Document for interactive git-clean says: "You also could say `c` or `clean` above as long as the choice is unique". But it's not true, because only hotkey `c` and full match (`clean`) could work. Implement partial matching via find_unique function to make the document right. Signed-off-by: Jiang Xin <worldhello.net@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jiang Xin committed Jul 24, 2013 at 10:22 UTC 60838613054f43d63086b0f4a7596baaee9e20c5
2 files changed +90 -30
builtin/clean.c
+52 -28
@@ -365,6 +365,56 @@ static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
365 string_list_clear(&menu_list, 0);
366 }
367
368 +static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
369 +{
370 + struct menu_item *menu_item;
371 + struct string_list_item *string_list_item;
372 + int i, len, found = 0;
373 +
374 + len = strlen(choice);
375 + switch (menu_stuff->type) {
376 + default:
377 + die("Bad type of menu_stuff when parse choice");
378 + case MENU_STUFF_TYPE_MENU_ITEM:
379 +
380 + menu_item = (struct menu_item *)menu_stuff->stuff;
381 + for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
382 + if (len == 1 && *choice == menu_item->hotkey) {
383 + found = i + 1;
384 + break;
385 + }
386 + if (!strncasecmp(choice, menu_item->title, len)) {
387 + if (found) {
388 + if (len == 1) {
389 + /* continue for hotkey matching */
390 + found = -1;
391 + } else {
392 + found = 0;
393 + break;
394 + }
395 + } else {
396 + found = i + 1;
397 + }
398 + }
399 + }
400 + break;
401 + case MENU_STUFF_TYPE_STRING_LIST:
402 + string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
403 + for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
404 + if (!strncasecmp(choice, string_list_item->string, len)) {
405 + if (found) {
406 + found = 0;
407 + break;
408 + }
409 + found = i + 1;
410 + }
411 + }
412 + break;
413 + }
414 + return found;
415 +}
416 +
417 +
418 /*
419 * Parse user input, and return choice(s) for menu (menu_stuff).
420 *
@@ -392,8 +442,6 @@ static int parse_choice(struct menu_stuff *menu_stuff,
442 int **chosen)
443 {
444 struct strbuf **choice_list, **ptr;
395 - struct menu_item *menu_item;
396 - struct string_list_item *string_list_item;
445 int nr = 0;
446 int i;
447
@@ -457,32 +505,8 @@ static int parse_choice(struct menu_stuff *menu_stuff,
505 bottom = 1;
506 top = menu_stuff->nr;
507 } else {
460 - switch (menu_stuff->type) {
461 - default:
462 - die("Bad type of menu_stuff when parse choice");
463 - case MENU_STUFF_TYPE_MENU_ITEM:
464 - menu_item = (struct menu_item *)menu_stuff->stuff;
465 - for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
466 - if (((*ptr)->len == 1 &&
467 - *(*ptr)->buf == menu_item->hotkey) ||
468 - !strcasecmp((*ptr)->buf, menu_item->title)) {
469 - bottom = i + 1;
470 - top = bottom;
471 - break;
472 - }
473 - }
474 - break;
475 - case MENU_STUFF_TYPE_STRING_LIST:
476 - string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
477 - for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
478 - if (!strcasecmp((*ptr)->buf, string_list_item->string)) {
479 - bottom = i + 1;
480 - top = bottom;
481 - break;
482 - }
483 - }
484 - break;
485 - }
508 + bottom = find_unique((*ptr)->buf, menu_stuff);
509 + top = bottom;
510 }
511
512 if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
t/t7301-clean-interactive.sh
+38 -2
@@ -17,7 +17,7 @@ test_expect_success 'setup' '
17
18 '
19
20 -test_expect_success 'git clean -i (clean)' '
20 +test_expect_success 'git clean -i (c: clean hotkey)' '
21
22 mkdir -p build docs &&
23 touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
@@ -38,12 +38,33 @@ test_expect_success 'git clean -i (clean)' '
38
39 '
40
41 +test_expect_success 'git clean -i (cl: clean prefix)' '
42 +
43 + mkdir -p build docs &&
44 + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
45 + docs/manual.txt obj.o build/lib.so &&
46 + echo cl | git clean -i &&
47 + test -f Makefile &&
48 + test -f README &&
49 + test -f src/part1.c &&
50 + test -f src/part2.c &&
51 + test ! -f a.out &&
52 + test -f docs/manual.txt &&
53 + test ! -f src/part3.c &&
54 + test ! -f src/part3.h &&
55 + test ! -f src/part4.c &&
56 + test ! -f src/part4.h &&
57 + test -f obj.o &&
58 + test -f build/lib.so
59 +
60 +'
61 +
62 test_expect_success 'git clean -i (quit)' '
63
64 mkdir -p build docs &&
65 touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
66 docs/manual.txt obj.o build/lib.so &&
46 - echo q | git clean -i &&
67 + echo quit | git clean -i &&
68 test -f Makefile &&
69 test -f README &&
70 test -f src/part1.c &&
@@ -256,6 +277,21 @@ test_expect_success 'git clean -id (select - number 3)' '
277
278 '
279
280 +test_expect_success 'git clean -id (select - filenames)' '
281 +
282 + mkdir -p build docs &&
283 + touch a.out foo.txt bar.txt baz.txt &&
284 + (echo s; echo a.out fo ba bar; echo; echo c) | \
285 + git clean -id &&
286 + test -f Makefile &&
287 + test ! -f a.out &&
288 + test ! -f foo.txt &&
289 + test ! -f bar.txt &&
290 + test -f baz.txt &&
291 + rm baz.txt
292 +
293 +'
294 +
295 test_expect_success 'git clean -id (select - range)' '
296
297 mkdir -p build docs &&