Skip to content

Commit cbd10da

Browse files
committed
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 <[email protected]>
1 parent 952fbc8 commit cbd10da

File tree

1 file changed

+84
-7
lines changed

1 file changed

+84
-7
lines changed

add-interactive.c

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "string-list.h"
99
#include "lockfile.h"
1010
#include "dir.h"
11+
#include "run-command.h"
1112

1213
struct add_i_state {
1314
struct repository *r;
@@ -375,7 +376,7 @@ static ssize_t list_and_choose(struct add_i_state *s,
375376

376377
struct adddel {
377378
uintmax_t add, del;
378-
unsigned seen:1, binary:1;
379+
unsigned seen:1, unmerged:1, binary:1;
379380
};
380381

381382
struct file_item {
@@ -415,6 +416,7 @@ struct collection_status {
415416
const char *reference;
416417

417418
unsigned skip_unseen:1;
419+
size_t unmerged_count, binary_count;
418420
struct string_list *files;
419421
struct hashmap file_map;
420422
};
@@ -437,7 +439,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
437439
int hash = strhash(name);
438440
struct pathname_entry *entry;
439441
struct file_item *file_item;
440-
struct adddel *adddel;
442+
struct adddel *adddel, *other_adddel;
441443

442444
entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
443445
struct pathname_entry, ent);
@@ -457,11 +459,21 @@ static void collect_changes_cb(struct diff_queue_struct *q,
457459
file_item = entry->item;
458460
adddel = s->mode == FROM_INDEX ?
459461
&file_item->index : &file_item->worktree;
462+
other_adddel = s->mode == FROM_INDEX ?
463+
&file_item->worktree : &file_item->index;
460464
adddel->seen = 1;
461465
adddel->add = stat.files[i]->added;
462466
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++;
464470
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+
}
465477
}
466478
free_diffstat_info(&stat);
467479
}
@@ -475,7 +487,9 @@ enum modified_files_filter {
475487
static int get_modified_files(struct repository *r,
476488
enum modified_files_filter filter,
477489
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)
479493
{
480494
struct object_id head_oid;
481495
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
@@ -525,6 +539,10 @@ static int get_modified_files(struct repository *r,
525539
clear_pathspec(&rev.prune_data);
526540
}
527541
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;
528546

529547
/* While the diffs are ordered already, we ran *two* diffs... */
530548
string_list_sort(&files->items);
@@ -607,7 +625,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
607625
struct prefix_item_list *files,
608626
struct list_and_choose_options *opts)
609627
{
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)
611629
return -1;
612630

613631
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,
624642
size_t count, i;
625643
struct lock_file index_lock;
626644

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)
628646
return -1;
629647

630648
if (!files->items.nr) {
@@ -703,7 +721,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
703721
struct tree *tree;
704722
struct diff_options diffopt = { NULL };
705723

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)
707725
return -1;
708726

709727
if (!files->items.nr) {
@@ -855,6 +873,64 @@ static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
855873
return res;
856874
}
857875

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+
858934
static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
859935
struct prefix_item_list *unused_files,
860936
struct list_and_choose_options *unused_opts)
@@ -952,6 +1028,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
9521028
{ "update", run_update },
9531029
{ "revert", run_revert },
9541030
{ "add untracked", run_add_untracked },
1031+
{ "patch", run_patch },
9551032
{ "help", run_help },
9561033
};
9571034
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;

0 commit comments

Comments
 (0)