Skip to content

Commit

Permalink
built-in add -i: implement the patch command
Browse files Browse the repository at this point in the history
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 <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
dscho authored and gitster committed Nov 18, 2019
1 parent daabf56 commit 9d86921
Showing 1 changed file with 84 additions and 7 deletions.
91 changes: 84 additions & 7 deletions add-interactive.c
Expand Up @@ -8,6 +8,7 @@
#include "string-list.h"
#include "lockfile.h"
#include "dir.h"
#include "run-command.h"

struct add_i_state {
struct repository *r;
Expand Down Expand Up @@ -375,7 +376,7 @@ static ssize_t list_and_choose(struct add_i_state *s,

struct adddel {
uintmax_t add, del;
unsigned seen:1, binary:1;
unsigned seen:1, unmerged:1, binary:1;
};

struct file_item {
Expand Down Expand Up @@ -415,6 +416,7 @@ struct collection_status {
const char *reference;

unsigned skip_unseen:1;
size_t unmerged_count, binary_count;
struct string_list *files;
struct hashmap file_map;
};
Expand All @@ -437,7 +439,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
int hash = strhash(name);
struct pathname_entry *entry;
struct file_item *file_item;
struct adddel *adddel;
struct adddel *adddel, *other_adddel;

entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
struct pathname_entry, ent);
Expand All @@ -457,11 +459,21 @@ static void collect_changes_cb(struct diff_queue_struct *q,
file_item = entry->item;
adddel = s->phase == FROM_INDEX ?
&file_item->index : &file_item->worktree;
other_adddel = s->phase == FROM_INDEX ?
&file_item->worktree : &file_item->index;
adddel->seen = 1;
adddel->add = stat.files[i]->added;
adddel->del = stat.files[i]->deleted;
if (stat.files[i]->is_binary)
if (stat.files[i]->is_binary) {
if (!other_adddel->binary)
s->binary_count++;
adddel->binary = 1;
}
if (stat.files[i]->is_unmerged) {
if (!other_adddel->unmerged)
s->unmerged_count++;
adddel->unmerged = 1;
}
}
free_diffstat_info(&stat);
}
Expand All @@ -475,7 +487,9 @@ enum modified_files_filter {
static int get_modified_files(struct repository *r,
enum modified_files_filter filter,
struct prefix_item_list *files,
const struct pathspec *ps)
const struct pathspec *ps,
size_t *unmerged_count,
size_t *binary_count)
{
struct object_id head_oid;
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
Expand Down Expand Up @@ -522,6 +536,10 @@ static int get_modified_files(struct repository *r,
}
}
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
if (unmerged_count)
*unmerged_count = s.unmerged_count;
if (binary_count)
*binary_count = s.binary_count;

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

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

if (get_modified_files(s->r, WORKTREE_ONLY, files, ps) < 0)
if (get_modified_files(s->r, WORKTREE_ONLY, files, ps, NULL, NULL) < 0)
return -1;

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

if (get_modified_files(s->r, INDEX_ONLY, files, ps) < 0)
if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
return -1;

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

static int run_patch(struct add_i_state *s, const struct pathspec *ps,
struct prefix_item_list *files,
struct list_and_choose_options *opts)
{
int res = 0;
ssize_t count, i, j;
size_t unmerged_count = 0, binary_count = 0;

if (get_modified_files(s->r, WORKTREE_ONLY, files, ps,
&unmerged_count, &binary_count) < 0)
return -1;

if (unmerged_count || binary_count) {
for (i = j = 0; i < files->items.nr; i++) {
struct file_item *item = files->items.items[i].util;

if (item->index.binary || item->worktree.binary) {
free(item);
free(files->items.items[i].string);
} else if (item->index.unmerged ||
item->worktree.unmerged) {
color_fprintf_ln(stderr, s->error_color,
_("ignoring unmerged: %s"),
files->items.items[i].string);
free(item);
free(files->items.items[i].string);
} else
files->items.items[j++] = files->items.items[i];
}
files->items.nr = j;
}

if (!files->items.nr) {
if (binary_count)
fprintf(stderr, _("Only binary files changed.\n"));
else
fprintf(stderr, _("No changes.\n"));
return 0;
}

opts->prompt = N_("Patch update");
count = list_and_choose(s, files, opts);
if (count >= 0) {
struct argv_array args = ARGV_ARRAY_INIT;

argv_array_pushl(&args, "git", "add--interactive", "--patch",
"--", NULL);
for (i = 0; i < files->items.nr; i++)
if (files->selected[i])
argv_array_push(&args,
files->items.items[i].string);
res = run_command_v_opt(args.argv, 0);
argv_array_clear(&args);
}

return res;
}

static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
struct prefix_item_list *unused_files,
struct list_and_choose_options *unused_opts)
Expand Down Expand Up @@ -949,6 +1025,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
{ "update", run_update },
{ "revert", run_revert },
{ "add untracked", run_add_untracked },
{ "patch", run_patch },
{ "help", run_help },
};
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
Expand Down

0 comments on commit 9d86921

Please sign in to comment.