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;