Skip to content

Commit 0c8a71e

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 5c49879 commit 0c8a71e

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;
@@ -374,7 +375,7 @@ static ssize_t list_and_choose(struct add_i_state *s,
374375

375376
struct adddel {
376377
uintmax_t add, del;
377-
unsigned seen:1, binary:1;
378+
unsigned seen:1, unmerged:1, binary:1;
378379
};
379380

380381
struct file_item {
@@ -414,6 +415,7 @@ struct collection_status {
414415
const char *reference;
415416

416417
unsigned skip_unseen:1;
418+
size_t unmerged_count, binary_count;
417419
struct string_list *files;
418420
struct hashmap file_map;
419421
};
@@ -436,7 +438,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
436438
int hash = strhash(name);
437439
struct pathname_entry *entry;
438440
struct file_item *file_item;
439-
struct adddel *adddel;
441+
struct adddel *adddel, *other_adddel;
440442

441443
entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
442444
struct pathname_entry, ent);
@@ -456,11 +458,21 @@ static void collect_changes_cb(struct diff_queue_struct *q,
456458
file_item = entry->item;
457459
adddel = s->phase == FROM_INDEX ?
458460
&file_item->index : &file_item->worktree;
461+
other_adddel = s->phase == FROM_INDEX ?
462+
&file_item->worktree : &file_item->index;
459463
adddel->seen = 1;
460464
adddel->add = stat.files[i]->added;
461465
adddel->del = stat.files[i]->deleted;
462-
if (stat.files[i]->is_binary)
466+
if (stat.files[i]->is_binary) {
467+
if (!other_adddel->binary)
468+
s->binary_count++;
463469
adddel->binary = 1;
470+
}
471+
if (stat.files[i]->is_unmerged) {
472+
if (!other_adddel->unmerged)
473+
s->unmerged_count++;
474+
adddel->unmerged = 1;
475+
}
464476
}
465477
free_diffstat_info(&stat);
466478
}
@@ -474,7 +486,9 @@ enum modified_files_filter {
474486
static int get_modified_files(struct repository *r,
475487
enum modified_files_filter filter,
476488
struct prefix_item_list *files,
477-
const struct pathspec *ps)
489+
const struct pathspec *ps,
490+
size_t *unmerged_count,
491+
size_t *binary_count)
478492
{
479493
struct object_id head_oid;
480494
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
@@ -521,6 +535,10 @@ static int get_modified_files(struct repository *r,
521535
}
522536
}
523537
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
538+
if (unmerged_count)
539+
*unmerged_count = s.unmerged_count;
540+
if (binary_count)
541+
*binary_count = s.binary_count;
524542

525543
/* While the diffs are ordered already, we ran *two* diffs... */
526544
string_list_sort(&files->items);
@@ -603,7 +621,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
603621
struct prefix_item_list *files,
604622
struct list_and_choose_options *opts)
605623
{
606-
if (get_modified_files(s->r, NO_FILTER, files, ps) < 0)
624+
if (get_modified_files(s->r, NO_FILTER, files, ps, NULL, NULL) < 0)
607625
return -1;
608626

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

623-
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
641+
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps, NULL, NULL) < 0)
624642
return -1;
625643

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

702-
if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
720+
if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
703721
return -1;
704722

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

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

0 commit comments

Comments
 (0)