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 <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
ab1e1cccaf
commit
8746e07277
|
@ -8,6 +8,7 @@
|
||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
#include "lockfile.h"
|
#include "lockfile.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
#include "run-command.h"
|
||||||
|
|
||||||
struct add_i_state {
|
struct add_i_state {
|
||||||
struct repository *r;
|
struct repository *r;
|
||||||
|
@ -375,7 +376,7 @@ static ssize_t list_and_choose(struct add_i_state *s,
|
||||||
|
|
||||||
struct adddel {
|
struct adddel {
|
||||||
uintmax_t add, del;
|
uintmax_t add, del;
|
||||||
unsigned seen:1, binary:1;
|
unsigned seen:1, unmerged:1, binary:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct file_item {
|
struct file_item {
|
||||||
|
@ -415,6 +416,7 @@ struct collection_status {
|
||||||
const char *reference;
|
const char *reference;
|
||||||
|
|
||||||
unsigned skip_unseen:1;
|
unsigned skip_unseen:1;
|
||||||
|
size_t unmerged_count, binary_count;
|
||||||
struct string_list *files;
|
struct string_list *files;
|
||||||
struct hashmap file_map;
|
struct hashmap file_map;
|
||||||
};
|
};
|
||||||
|
@ -437,7 +439,7 @@ static void collect_changes_cb(struct diff_queue_struct *q,
|
||||||
int hash = strhash(name);
|
int hash = strhash(name);
|
||||||
struct pathname_entry *entry;
|
struct pathname_entry *entry;
|
||||||
struct file_item *file_item;
|
struct file_item *file_item;
|
||||||
struct adddel *adddel;
|
struct adddel *adddel, *other_adddel;
|
||||||
|
|
||||||
entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
|
entry = hashmap_get_entry_from_hash(&s->file_map, hash, name,
|
||||||
struct pathname_entry, ent);
|
struct pathname_entry, ent);
|
||||||
|
@ -457,11 +459,21 @@ static void collect_changes_cb(struct diff_queue_struct *q,
|
||||||
file_item = entry->item;
|
file_item = entry->item;
|
||||||
adddel = s->mode == FROM_INDEX ?
|
adddel = s->mode == FROM_INDEX ?
|
||||||
&file_item->index : &file_item->worktree;
|
&file_item->index : &file_item->worktree;
|
||||||
|
other_adddel = s->mode == FROM_INDEX ?
|
||||||
|
&file_item->worktree : &file_item->index;
|
||||||
adddel->seen = 1;
|
adddel->seen = 1;
|
||||||
adddel->add = stat.files[i]->added;
|
adddel->add = stat.files[i]->added;
|
||||||
adddel->del = stat.files[i]->deleted;
|
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;
|
adddel->binary = 1;
|
||||||
|
}
|
||||||
|
if (stat.files[i]->is_unmerged) {
|
||||||
|
if (!other_adddel->unmerged)
|
||||||
|
s->unmerged_count++;
|
||||||
|
adddel->unmerged = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free_diffstat_info(&stat);
|
free_diffstat_info(&stat);
|
||||||
}
|
}
|
||||||
|
@ -475,7 +487,9 @@ enum modified_files_filter {
|
||||||
static int get_modified_files(struct repository *r,
|
static int get_modified_files(struct repository *r,
|
||||||
enum modified_files_filter filter,
|
enum modified_files_filter filter,
|
||||||
struct prefix_item_list *files,
|
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;
|
struct object_id head_oid;
|
||||||
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
|
int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
|
||||||
|
@ -525,6 +539,10 @@ static int get_modified_files(struct repository *r,
|
||||||
clear_pathspec(&rev.prune_data);
|
clear_pathspec(&rev.prune_data);
|
||||||
}
|
}
|
||||||
hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
|
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... */
|
/* While the diffs are ordered already, we ran *two* diffs... */
|
||||||
string_list_sort(&files->items);
|
string_list_sort(&files->items);
|
||||||
|
@ -607,7 +625,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
|
||||||
struct prefix_item_list *files,
|
struct prefix_item_list *files,
|
||||||
struct list_and_choose_options *opts)
|
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;
|
return -1;
|
||||||
|
|
||||||
list(s, &files->items, NULL, &opts->list_opts);
|
list(s, &files->items, NULL, &opts->list_opts);
|
||||||
|
@ -624,7 +642,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
|
||||||
size_t count, i;
|
size_t count, i;
|
||||||
struct lock_file index_lock;
|
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;
|
return -1;
|
||||||
|
|
||||||
if (!files->items.nr) {
|
if (!files->items.nr) {
|
||||||
|
@ -703,7 +721,7 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
|
||||||
struct tree *tree;
|
struct tree *tree;
|
||||||
struct diff_options diffopt = { NULL };
|
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;
|
return -1;
|
||||||
|
|
||||||
if (!files->items.nr) {
|
if (!files->items.nr) {
|
||||||
|
@ -855,6 +873,64 @@ finish_add_untracked:
|
||||||
return res;
|
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,
|
static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
|
||||||
struct prefix_item_list *unused_files,
|
struct prefix_item_list *unused_files,
|
||||||
struct list_and_choose_options *unused_opts)
|
struct list_and_choose_options *unused_opts)
|
||||||
|
@ -952,6 +1028,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
|
||||||
{ "update", run_update },
|
{ "update", run_update },
|
||||||
{ "revert", run_revert },
|
{ "revert", run_revert },
|
||||||
{ "add untracked", run_add_untracked },
|
{ "add untracked", run_add_untracked },
|
||||||
|
{ "patch", run_patch },
|
||||||
{ "help", run_help },
|
{ "help", run_help },
|
||||||
};
|
};
|
||||||
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
|
struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT;
|
||||||
|
|
Loading…
Reference in New Issue