@matheusbsbs / matheus-bagliano / commits / 8746e07277
Fork 0

built-in add -i: implement the `patch` command

Well, it is not a full implementation yet. In the interest of making this easy to review (and easy to keep bugs out), we still hand off to the Perl script to do the actual work. The `patch` functionality actually makes up for more than half of the 1,800+ lines of `git-add--interactive.perl`. It will be ported from Perl to C incrementally, later. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Nov 29, 2019 at 21:11 UTC 8746e07277cb548185a33efa0037c313a06001f8
1 file changed +84 -7
add-interactive.c
+84 -7
@@ -8,6 +8,7 @@
8 #include "string-list.h"
9 #include "lockfile.h"
10 #include "dir.h"
11 +#include "run-command.h"
12
13 struct add_i_state {
14 struct repository *r;
@@ -375,7 +376,7 @@ static ssize_t list_and_choose(struct add_i_state *s,
376
377 struct adddel {
378 uintmax_t add, del;
378 - unsigned seen:1, binary:1;
379 + unsigned seen:1, unmerged:1, binary:1;
380 };
381
382 struct file_item {
@@ -415,6 +416,7 @@ struct collection_status {
416 const char *reference;
417
418 unsigned skip_unseen:1;
419 + size_t unmerged_count, binary_count;
420 struct string_list *files;
421 struct hashmap file_map;
422 };
@@ -437,7 +439,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
439 int hash = strhash(name);
440 struct pathname_entry *entry;
441 struct file_item *file_item;
440 - struct adddel *adddel;
442 + struct adddel *adddel, *other_adddel;
443
444 entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
445 struct pathname_entry, ent);
@@ -457,11 +459,21 @@ static void collect_changes_cb(struct diff_queue_struct *q,
459 file_item = entry->item;
460 adddel = s->mode == FROM_INDEX ?
461 &file_item->index : &file_item->worktree;
462 + other_adddel = s->mode == FROM_INDEX ?
463 + &file_item->worktree : &file_item->index;
464 adddel->seen = 1;
465 adddel->add = stat.files[i]->added;
466 adddel->del = stat.files[i]->deleted;
463 - if (stat.files[i]->is_binary)
467 + if (stat.files[i]->is_binary) {
468 + if (!other_adddel->binary)
469 + s->binary_count++;
470 adddel->binary = 1;
471 + }
472 + if (stat.files[i]->is_unmerged) {
473 + if (!other_adddel->unmerged)
474 + s->unmerged_count++;
475 + adddel->unmerged = 1;
476 + }
477 }
478 free_diffstat_info(&stat);
479 }
@@ -475,7 +487,9 @@ enum modified_files_filter {
487 static int get_modified_files(struct repository *r,
488 enum modified_files_filter filter,
489 struct prefix_item_list *files,
478 - const struct pathspec *ps)
490 + const struct pathspec *ps,
491 + size_t *unmerged_count,
492 + size_t *binary_count)
493 {
494 struct object_id head_oid;
495 int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
@@ -525,6 +539,10 @@ static int get_modified_files(struct repository *r,
539 clear_pathspec(&rev.prune_data);
540 }
541 hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
542 + if (unmerged_count)
543 + *unmerged_count = s.unmerged_count;
544 + if (binary_count)
545 + *binary_count = s.binary_count;
546
547 /* While the diffs are ordered already, we ran *two* diffs... */
548 string_list_sort(&files->items);
@@ -607,7 +625,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
625 struct prefix_item_list *files,
626 struct list_and_choose_options *opts)
627 {
610 - if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
628 + if (get_modified_files(s->r, NO_FILTER, files, ps, NULL, NULL) < 0)
629 return -1;
630
631 list(s, &files->items, NULL, &opts->list_opts);
@@ -624,7 +642,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
642 size_t count, i;
643 struct lock_file index_lock;
644
627 - if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
645 + if (get_modified_files(s->r, WORKTREE_ONLY, files, ps, NULL, NULL) < 0)
646 return -1;
647
648 if (!files->items.nr) {
@@ -703,7 +721,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
721 struct tree *tree;
722 struct diff_options diffopt = { NULL };
723
706 - if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
724 + if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
725 return -1;
726
727 if (!files->items.nr) {
@@ -855,6 +873,64 @@ finish_add_untracked:
873 return res;
874 }
875
876 +static int run_patch(struct add_i_state *s, const struct pathspec *ps,
877 + struct prefix_item_list *files,
878 + struct list_and_choose_options *opts)
879 +{
880 + int res = 0;
881 + ssize_t count, i, j;
882 + size_t unmerged_count = 0, binary_count = 0;
883 +
884 + if (get_modified_files(s->r, WORKTREE_ONLY, files, ps,
885 + &unmerged_count, &binary_count) < 0)
886 + return -1;
887 +
888 + if (unmerged_count || binary_count) {
889 + for (i = j = 0; i < files->items.nr; i++) {
890 + struct file_item *item = files->items.items[i].util;
891 +
892 + if (item->index.binary || item->worktree.binary) {
893 + free(item);
894 + free(files->items.items[i].string);
895 + } else if (item->index.unmerged ||
896 + item->worktree.unmerged) {
897 + color_fprintf_ln(stderr, s->error_color,
898 + _("ignoring unmerged: %s"),
899 + files->items.items[i].string);
900 + free(item);
901 + free(files->items.items[i].string);
902 + } else
903 + files->items.items[j++] = files->items.items[i];
904 + }
905 + files->items.nr = j;
906 + }
907 +
908 + if (!files->items.nr) {
909 + if (binary_count)
910 + fprintf(stderr, _("Only binary files changed.\n"));
911 + else
912 + fprintf(stderr, _("No changes.\n"));
913 + return 0;
914 + }
915 +
916 + opts->prompt = N_("Patch update");
917 + count = list_and_choose(s, files, opts);
918 + if (count >= 0) {
919 + struct argv_array args = ARGV_ARRAY_INIT;
920 +
921 + argv_array_pushl(&args, "git", "add--interactive", "--patch",
922 + "--", NULL);
923 + for (i = 0; i < files->items.nr; i++)
924 + if (files->selected[i])
925 + argv_array_push(&args,
926 + files->items.items[i].string);
927 + res = run_command_v_opt(args.argv, 0);
928 + argv_array_clear(&args);
929 + }
930 +
931 + return res;
932 +}
933 +
934 static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
935 struct prefix_item_list *unused_files,
936 struct list_and_choose_options *unused_opts)
@@ -952,6 +1028,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
1028 { "update", run_update },
1029 { "revert", run_revert },
1030 { "add untracked", run_add_untracked },
1031 + { "patch", run_patch },
1032 { "help", run_help },
1033 };
1034 struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;