Skip to content

Commit 9d86921

Browse files
dschogitster
authored andcommitted
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]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent daabf56 commit 9d86921

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->phase == FROM_INDEX ?
459461
&file_item->index : &file_item->worktree;
462+
other_adddel = s->phase == 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,
@@ -522,6 +536,10 @@ static int get_modified_files(struct repository *r,
522536
}
523537
}
524538
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
539+
if (unmerged_count)
540+
*unmerged_count = s.unmerged_count;
541+
if (binary_count)
542+
*binary_count = s.binary_count;
525543

526544
/* While the diffs are ordered already, we ran *two* diffs... */
527545
string_list_sort(&files->items);
@@ -604,7 +622,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
604622
struct prefix_item_list *files,
605623
struct list_and_choose_options *opts)
606624
{
607-
if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
625+
if (get_modified_files(s->r, NO_FILTER, files, ps, NULL, NULL) < 0)
608626
return -1;
609627

610628
list(s, &files->items, NULL, &opts->list_opts);
@@ -621,7 +639,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
621639
size_t count, i;
622640
struct lock_file index_lock;
623641

624-
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
642+
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps, NULL, NULL) < 0)
625643
return -1;
626644

627645
if (!files->items.nr) {
@@ -700,7 +718,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
700718
struct tree *tree;
701719
struct diff_options diffopt = { NULL };
702720

703-
if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
721+
if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
704722
return -1;
705723

706724
if (!files->items.nr) {
@@ -852,6 +870,64 @@ static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
852870
return res;
853871
}
854872

873+
static int run_patch(struct add_i_state *s, const struct pathspec *ps,
874+
struct prefix_item_list *files,
875+
struct list_and_choose_options *opts)
876+
{
877+
int res = 0;
878+
ssize_t count, i, j;
879+
size_t unmerged_count = 0, binary_count = 0;
880+
881+
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps,
882+
&unmerged_count, &binary_count) < 0)
883+
return -1;
884+
885+
if (unmerged_count || binary_count) {
886+
for (i = j = 0; i < files->items.nr; i++) {
887+
struct file_item *item = files->items.items[i].util;
888+
889+
if (item->index.binary || item->worktree.binary) {
890+
free(item);
891+
free(files->items.items[i].string);
892+
} else if (item->index.unmerged ||
893+
item->worktree.unmerged) {
894+
color_fprintf_ln(stderr, s->error_color,
895+
_("ignoring unmerged: %s"),
896+
files->items.items[i].string);
897+
free(item);
898+
free(files->items.items[i].string);
899+
} else
900+
files->items.items[j++] = files->items.items[i];
901+
}
902+
files->items.nr = j;
903+
}
904+
905+
if (!files->items.nr) {
906+
if (binary_count)
907+
fprintf(stderr, _("Only binary files changed.\n"));
908+
else
909+
fprintf(stderr, _("No changes.\n"));
910+
return 0;
911+
}
912+
913+
opts->prompt = N_("Patch update");
914+
count = list_and_choose(s, files, opts);
915+
if (count >= 0) {
916+
struct argv_array args = ARGV_ARRAY_INIT;
917+
918+
argv_array_pushl(&args, "git", "add--interactive", "--patch",
919+
"--", NULL);
920+
for (i = 0; i < files->items.nr; i++)
921+
if (files->selected[i])
922+
argv_array_push(&args,
923+
files->items.items[i].string);
924+
res = run_command_v_opt(args.argv, 0);
925+
argv_array_clear(&args);
926+
}
927+
928+
return res;
929+
}
930+
855931
static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
856932
struct prefix_item_list *unused_files,
857933
struct list_and_choose_options *unused_opts)
@@ -949,6 +1025,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
9491025
{ "update", run_update },
9501026
{ "revert", run_revert },
9511027
{ "add untracked", run_add_untracked },
1028+
{ "patch", run_patch },
9521029
{ "help", run_help },
9531030
};
9541031
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;

0 commit comments

Comments
 (0)