Skip to content

Commit c6b4de4

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 4d8a4e6 commit c6b4de4

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;
@@ -255,7 +257,7 @@ static ssize_t list_and_choose(struct prefix_item **items, int *selected,
255257

256258
struct adddel {
257259
uintmax_t add, del;
258-
unsigned seen:1, binary:1;
260+
unsigned seen:1, unmerged:1, binary:1;
259261
};
260262

261263
struct file_list {
@@ -322,6 +324,7 @@ struct collection_status {
322324
const char *reference;
323325

324326
unsigned skip_unseen:1;
327+
size_t unmerged_count, binary_count;
325328
struct file_list *list;
326329
struct hashmap file_map;
327330
};
@@ -345,12 +348,14 @@ static void collect_changes_cb(struct diff_queue_struct *q,
345348
struct pathname_entry *entry;
346349
size_t file_index;
347350
struct file_item *file;
348-
struct adddel *adddel;
351+
struct adddel *adddel, *other_adddel;
349352

350353
entry = hashmap_get_from_hash(&s->file_map, hash, name);
351-
if (entry)
354+
if (entry) {
355+
if (entry->index == (size_t)-1)
356+
continue;
352357
file_index = entry->index;
353-
else if (s->skip_unseen)
358+
} else if (s->skip_unseen)
354359
continue;
355360
else {
356361
FLEX_ALLOC_STR(entry, pathname, name);
@@ -363,11 +368,20 @@ static void collect_changes_cb(struct diff_queue_struct *q,
363368
file = s->list->file[file_index];
364369

365370
adddel = s->phase == FROM_INDEX ? &file->index : &file->worktree;
371+
other_adddel = s->phase == FROM_INDEX ? &file->worktree : &file->index;
366372
adddel->seen = 1;
367373
adddel->add = stat.files[i]->added;
368374
adddel->del = stat.files[i]->deleted;
369-
if (stat.files[i]->is_binary)
375+
if (stat.files[i]->is_binary) {
376+
if (!other_adddel->binary)
377+
s->binary_count++;
370378
adddel->binary = 1;
379+
}
380+
if (stat.files[i]->is_unmerged) {
381+
if (!other_adddel->unmerged)
382+
s->unmerged_count++;
383+
adddel->unmerged = 1;
384+
}
371385
}
372386
}
373387

@@ -379,6 +393,8 @@ enum modified_files_filter {
379393

380394
static int get_modified_files(struct repository *r,
381395
enum modified_files_filter filter,
396+
size_t *unmerged_count,
397+
size_t *binary_count,
382398
struct file_list *list,
383399
const struct pathspec *ps)
384400
{
@@ -425,6 +441,10 @@ static int get_modified_files(struct repository *r,
425441
}
426442
}
427443
hashmap_free(&s.file_map, 1);
444+
if (unmerged_count)
445+
*unmerged_count = s.unmerged_count;
446+
if (binary_count)
447+
*binary_count = s.binary_count;
428448

429449
/* While the diffs are ordered already, we ran *two* diffs... */
430450
QSORT(list->file, list->nr, file_item_cmp);
@@ -509,7 +529,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
509529
{
510530
reset_file_list(files);
511531

512-
if (get_modified_files(s->r, 0, files, ps) < 0)
532+
if (get_modified_files(s->r, 0, NULL, NULL, files, ps) < 0)
513533
return -1;
514534

515535
if (files->nr)
@@ -530,7 +550,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
530550

531551
reset_file_list(files);
532552

533-
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
553+
if (get_modified_files(s->r, WORKTREE_ONLY, NULL, NULL, files, ps) < 0)
534554
return -1;
535555

536556
if (!files->nr) {
@@ -616,7 +636,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
616636
struct diff_options diffopt = { NULL };
617637

618638
reset_file_list(files);
619-
if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
639+
if (get_modified_files(s->r, INDEX_ONLY, NULL, NULL, files, ps) < 0)
620640
return -1;
621641

622642
if (!files->nr) {
@@ -774,6 +794,64 @@ static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
774794
return res;
775795
}
776796

797+
static int run_patch(struct add_i_state *s, const struct pathspec *ps,
798+
struct file_list *files,
799+
struct list_and_choose_options *opts)
800+
{
801+
struct prefix_item **items = (struct prefix_item **)files->file;
802+
int res = 0, *selected = NULL;
803+
ssize_t count, i, j;
804+
size_t unmerged_count = 0, binary_count = 0;
805+
806+
reset_file_list(files);
807+
if (get_modified_files(s->r, WORKTREE_ONLY, &unmerged_count,
808+
&binary_count, files, ps) < 0)
809+
return -1;
810+
811+
if (unmerged_count || binary_count) {
812+
for (i = j = 0; i < files->nr; i++)
813+
if (files->file[i]->index.binary ||
814+
files->file[i]->worktree.binary)
815+
free(items[i]);
816+
else if (files->file[i]->index.unmerged ||
817+
files->file[i]->worktree.unmerged) {
818+
color_fprintf_ln(stderr, s->error_color,
819+
_("ignoring unmerged: %s"),
820+
files->file[i]->item.name);
821+
free(items[i]);
822+
} else
823+
items[j++] = items[i];
824+
files->nr = j;
825+
}
826+
827+
if (!files->nr) {
828+
if (binary_count)
829+
fprintf(stderr, _("Only binary files changed.\n"));
830+
else
831+
fprintf(stderr, _("No changes.\n"));
832+
return 0;
833+
}
834+
835+
opts->prompt = N_("Patch update");
836+
CALLOC_ARRAY(selected, files->nr);
837+
838+
count = list_and_choose(items, selected, files->nr, s, opts);
839+
if (count >= 0) {
840+
struct argv_array args = ARGV_ARRAY_INIT;
841+
842+
argv_array_pushl(&args, "git", "add--interactive", "--patch",
843+
"--", NULL);
844+
for (i = 0; i < files->nr; i++)
845+
if (selected[i])
846+
argv_array_push(&args, items[i]->name);
847+
res = run_command_v_opt(args.argv, 0);
848+
argv_array_clear(&args);
849+
}
850+
851+
free(selected);
852+
return res;
853+
}
854+
777855
static int run_help(struct add_i_state *s, const struct pathspec *ps,
778856
struct file_list *files,
779857
struct list_and_choose_options *opts)
@@ -867,10 +945,11 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
867945
update = { { "update" }, run_update },
868946
revert = { { "revert" }, run_revert },
869947
add_untracked = { { "add untracked" }, run_add_untracked },
948+
patch = { { "patch" }, run_patch },
870949
help = { { "help" }, run_help };
871950
struct command_item *commands[] = {
872951
&status, &update, &revert, &add_untracked,
873-
&help
952+
&patch, &help
874953
};
875954

876955
struct print_file_item_data print_file_item_data = {

0 commit comments

Comments
 (0)