Skip to content

Commit c599406

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 3df0c46 commit c599406

File tree

1 file changed

+88
-9
lines changed

1 file changed

+88
-9
lines changed

add-interactive.c

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include "lockfile.h"
1010
#include "pathspec.h"
1111
#include "dir.h"
12+
#include "argv-array.h"
13+
#include "run-command.h"
1214

1315
struct add_i_state {
1416
struct repository *r;
@@ -248,7 +250,7 @@ static ssize_t list_and_choose(struct prefix_item **items, int *selected,
248250

249251
struct adddel {
250252
uintmax_t add, del;
251-
unsigned seen:1, binary:1;
253+
unsigned seen:1, unmerged:1, binary:1;
252254
};
253255

254256
struct file_list {
@@ -315,6 +317,7 @@ struct collection_status {
315317
const char *reference;
316318

317319
unsigned skip_unseen:1;
320+
size_t unmerged_count, binary_count;
318321
struct file_list *list;
319322
struct hashmap file_map;
320323
};
@@ -338,12 +341,14 @@ static void collect_changes_cb(struct diff_queue_struct *q,
338341
struct pathname_entry *entry;
339342
size_t file_index;
340343
struct file_item *file;
341-
struct adddel *adddel;
344+
struct adddel *adddel, *other_adddel;
342345

343346
entry = hashmap_get_from_hash(&s->file_map, hash, name);
344-
if (entry)
347+
if (entry) {
348+
if (entry->index == (size_t)-1)
349+
continue;
345350
file_index = entry->index;
346-
else if (s->skip_unseen)
351+
} else if (s->skip_unseen)
347352
continue;
348353
else {
349354
FLEX_ALLOC_STR(entry, pathname, name);
@@ -356,11 +361,20 @@ static void collect_changes_cb(struct diff_queue_struct *q,
356361
file = s->list->file[file_index];
357362

358363
adddel = s->phase == FROM_INDEX ? &file->index : &file->worktree;
364+
other_adddel = s->phase == FROM_INDEX ? &file->worktree : &file->index;
359365
adddel->seen = 1;
360366
adddel->add = stat.files[i]->added;
361367
adddel->del = stat.files[i]->deleted;
362-
if (stat.files[i]->is_binary)
368+
if (stat.files[i]->is_binary) {
369+
if (!other_adddel->binary)
370+
s->binary_count++;
363371
adddel->binary = 1;
372+
}
373+
if (stat.files[i]->is_unmerged) {
374+
if (!other_adddel->unmerged)
375+
s->unmerged_count++;
376+
adddel->unmerged = 1;
377+
}
364378
}
365379
}
366380

@@ -372,6 +386,8 @@ enum modified_files_filter {
372386

373387
static int get_modified_files(struct repository *r,
374388
enum modified_files_filter filter,
389+
size_t *unmerged_count,
390+
size_t *binary_count,
375391
struct file_list *list,
376392
const struct pathspec *ps)
377393
{
@@ -418,6 +434,10 @@ static int get_modified_files(struct repository *r,
418434
}
419435
}
420436
hashmap_free(&s.file_map, 1);
437+
if (unmerged_count)
438+
*unmerged_count = s.unmerged_count;
439+
if (binary_count)
440+
*binary_count = s.binary_count;
421441

422442
/* While the diffs are ordered already, we ran *two* diffs... */
423443
QSORT(list->file, list->nr, file_item_cmp);
@@ -502,7 +522,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
502522
{
503523
reset_file_list(files);
504524

505-
if (get_modified_files(s->r, 0, files, ps) < 0)
525+
if (get_modified_files(s->r, 0, NULL, NULL, files, ps) < 0)
506526
return -1;
507527

508528
if (files->nr)
@@ -523,7 +543,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
523543

524544
reset_file_list(files);
525545

526-
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
546+
if (get_modified_files(s->r, WORKTREE_ONLY, NULL, NULL, files, ps) < 0)
527547
return -1;
528548

529549
if (!files->nr) {
@@ -609,7 +629,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
609629
struct diff_options diffopt = { NULL };
610630

611631
reset_file_list(files);
612-
if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
632+
if (get_modified_files(s->r, INDEX_ONLY, NULL, NULL, files, ps) < 0)
613633
return -1;
614634

615635
if (!files->nr) {
@@ -767,6 +787,64 @@ static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
767787
return res;
768788
}
769789

790+
static int run_patch(struct add_i_state *s, const struct pathspec *ps,
791+
struct file_list *files,
792+
struct list_and_choose_options *opts)
793+
{
794+
struct prefix_item **items = (struct prefix_item **)files->file;
795+
int res = 0, *selected = NULL;
796+
ssize_t count, i, j;
797+
size_t unmerged_count = 0, binary_count = 0;
798+
799+
reset_file_list(files);
800+
if (get_modified_files(s->r, WORKTREE_ONLY, &unmerged_count,
801+
&binary_count, files, ps) < 0)
802+
return -1;
803+
804+
if (unmerged_count || binary_count) {
805+
for (i = j = 0; i < files->nr; i++)
806+
if (files->file[i]->index.binary ||
807+
files->file[i]->worktree.binary)
808+
free(items[i]);
809+
else if (files->file[i]->index.unmerged ||
810+
files->file[i]->worktree.unmerged) {
811+
color_fprintf_ln(stderr, s->error_color,
812+
_("ignoring unmerged: %s"),
813+
files->file[i]->item.name);
814+
free(items[i]);
815+
} else
816+
items[j++] = items[i];
817+
files->nr = j;
818+
}
819+
820+
if (!files->nr) {
821+
if (binary_count)
822+
fprintf(stderr, _("Only binary files changed.\n"));
823+
else
824+
fprintf(stderr, _("No changes.\n"));
825+
return 0;
826+
}
827+
828+
opts->prompt = N_("Patch update");
829+
CALLOC_ARRAY(selected, files->nr);
830+
831+
count = list_and_choose(items, selected, files->nr, s, opts);
832+
if (count >= 0) {
833+
struct argv_array args = ARGV_ARRAY_INIT;
834+
835+
argv_array_pushl(&args, "git", "add--interactive", "--patch",
836+
"--", NULL);
837+
for (i = 0; i < files->nr; i++)
838+
if (selected[i])
839+
argv_array_push(&args, items[i]->name);
840+
res = run_command_v_opt(args.argv, 0);
841+
argv_array_clear(&args);
842+
}
843+
844+
free(selected);
845+
return res;
846+
}
847+
770848
static int run_help(struct add_i_state *s, const struct pathspec *ps,
771849
struct file_list *files,
772850
struct list_and_choose_options *opts)
@@ -860,10 +938,11 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
860938
update = { { "update" }, run_update },
861939
revert = { { "revert" }, run_revert },
862940
add_untracked = { { "add untracked" }, run_add_untracked },
941+
patch = { { "patch" }, run_patch },
863942
help = { { "help" }, run_help };
864943
struct command_item *commands[] = {
865944
&status, &update, &revert, &add_untracked,
866-
&help
945+
&patch, &help
867946
};
868947

869948
struct print_file_item_data print_file_item_data = {

0 commit comments

Comments
 (0)