mirror of
https://github.com/git/git
synced 2024-08-24 18:26:02 +00:00
Merge branch 'js/add-p-in-c'
The effort to move "git-add--interactive" to C continues. * js/add-p-in-c: built-in add -p: show helpful hint when nothing can be staged built-in add -p: only show the applicable parts of the help text built-in add -p: implement the 'q' ("quit") command built-in add -p: implement the '/' ("search regex") command built-in add -p: implement the 'g' ("goto") command built-in add -p: implement hunk editing strbuf: add a helper function to call the editor "on an strbuf" built-in add -p: coalesce hunks after splitting them built-in add -p: implement the hunk splitting feature built-in add -p: show different prompts for mode changes and deletions built-in app -p: allow selecting a mode change as a "hunk" built-in add -p: handle deleted empty files built-in add -p: support multi-file diffs built-in add -p: offer a helpful error message when hunk navigation failed built-in add -p: color the prompt and the help text built-in add -p: adjust hunk headers as needed built-in add -p: show colored hunks by default built-in add -i: wire up the new C code for the `patch` command built-in add -i: start implementing the `patch` functionality in C
This commit is contained in:
commit
45b96a6fa1
1
Makefile
1
Makefile
|
@ -825,6 +825,7 @@ LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentat
|
|||
|
||||
LIB_OBJS += abspath.o
|
||||
LIB_OBJS += add-interactive.o
|
||||
LIB_OBJS += add-patch.o
|
||||
LIB_OBJS += advice.o
|
||||
LIB_OBJS += alias.o
|
||||
LIB_OBJS += alloc.o
|
||||
|
|
|
@ -10,16 +10,6 @@
|
|||
#include "dir.h"
|
||||
#include "run-command.h"
|
||||
|
||||
struct add_i_state {
|
||||
struct repository *r;
|
||||
int use_color;
|
||||
char header_color[COLOR_MAXLEN];
|
||||
char help_color[COLOR_MAXLEN];
|
||||
char prompt_color[COLOR_MAXLEN];
|
||||
char error_color[COLOR_MAXLEN];
|
||||
char reset_color[COLOR_MAXLEN];
|
||||
};
|
||||
|
||||
static void init_color(struct repository *r, struct add_i_state *s,
|
||||
const char *slot_name, char *dst,
|
||||
const char *default_color)
|
||||
|
@ -36,7 +26,7 @@ static void init_color(struct repository *r, struct add_i_state *s,
|
|||
free(key);
|
||||
}
|
||||
|
||||
static void init_add_i_state(struct add_i_state *s, struct repository *r)
|
||||
void init_add_i_state(struct add_i_state *s, struct repository *r)
|
||||
{
|
||||
const char *value;
|
||||
|
||||
|
@ -54,6 +44,14 @@ static void init_add_i_state(struct add_i_state *s, struct repository *r)
|
|||
init_color(r, s, "prompt", s->prompt_color, GIT_COLOR_BOLD_BLUE);
|
||||
init_color(r, s, "error", s->error_color, GIT_COLOR_BOLD_RED);
|
||||
init_color(r, s, "reset", s->reset_color, GIT_COLOR_RESET);
|
||||
init_color(r, s, "fraginfo", s->fraginfo_color,
|
||||
diff_get_color(s->use_color, DIFF_FRAGINFO));
|
||||
init_color(r, s, "context", s->context_color,
|
||||
diff_get_color(s->use_color, DIFF_CONTEXT));
|
||||
init_color(r, s, "old", s->file_old_color,
|
||||
diff_get_color(s->use_color, DIFF_FILE_OLD));
|
||||
init_color(r, s, "new", s->file_new_color,
|
||||
diff_get_color(s->use_color, DIFF_FILE_NEW));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -917,15 +915,18 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
|
|||
count = list_and_choose(s, files, opts);
|
||||
if (count >= 0) {
|
||||
struct argv_array args = ARGV_ARRAY_INIT;
|
||||
struct pathspec ps_selected = { 0 };
|
||||
|
||||
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);
|
||||
parse_pathspec(&ps_selected,
|
||||
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
|
||||
PATHSPEC_LITERAL_PATH, "", args.argv);
|
||||
res = run_add_p(s->r, &ps_selected);
|
||||
argv_array_clear(&args);
|
||||
clear_pathspec(&ps_selected);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
@ -1,8 +1,27 @@
|
|||
#ifndef ADD_INTERACTIVE_H
|
||||
#define ADD_INTERACTIVE_H
|
||||
|
||||
#include "color.h"
|
||||
|
||||
struct add_i_state {
|
||||
struct repository *r;
|
||||
int use_color;
|
||||
char header_color[COLOR_MAXLEN];
|
||||
char help_color[COLOR_MAXLEN];
|
||||
char prompt_color[COLOR_MAXLEN];
|
||||
char error_color[COLOR_MAXLEN];
|
||||
char reset_color[COLOR_MAXLEN];
|
||||
char fraginfo_color[COLOR_MAXLEN];
|
||||
char context_color[COLOR_MAXLEN];
|
||||
char file_old_color[COLOR_MAXLEN];
|
||||
char file_new_color[COLOR_MAXLEN];
|
||||
};
|
||||
|
||||
void init_add_i_state(struct add_i_state *s, struct repository *r);
|
||||
|
||||
struct repository;
|
||||
struct pathspec;
|
||||
int run_add_i(struct repository *r, const struct pathspec *ps);
|
||||
int run_add_p(struct repository *r, const struct pathspec *ps);
|
||||
|
||||
#endif
|
||||
|
|
1338
add-patch.c
Normal file
1338
add-patch.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -191,12 +191,17 @@ int run_add_interactive(const char *revision, const char *patch_mode,
|
|||
int use_builtin_add_i =
|
||||
git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
|
||||
|
||||
if (!patch_mode) {
|
||||
if (use_builtin_add_i < 0)
|
||||
git_config_get_bool("add.interactive.usebuiltin",
|
||||
&use_builtin_add_i);
|
||||
if (use_builtin_add_i == 1)
|
||||
if (use_builtin_add_i < 0)
|
||||
git_config_get_bool("add.interactive.usebuiltin",
|
||||
&use_builtin_add_i);
|
||||
|
||||
if (use_builtin_add_i == 1) {
|
||||
if (!patch_mode)
|
||||
return !!run_add_i(the_repository, pathspec);
|
||||
if (strcmp(patch_mode, "--patch"))
|
||||
die("'%s' not yet supported in the built-in add -p",
|
||||
patch_mode);
|
||||
return !!run_add_p(the_repository, pathspec);
|
||||
}
|
||||
|
||||
argv_array_push(&argv, "add--interactive");
|
||||
|
|
28
strbuf.c
28
strbuf.c
|
@ -1125,3 +1125,31 @@ int strbuf_normalize_path(struct strbuf *src)
|
|||
strbuf_release(&dst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
|
||||
const char *const *env)
|
||||
{
|
||||
char *path2 = NULL;
|
||||
int fd, res = 0;
|
||||
|
||||
if (!is_absolute_path(path))
|
||||
path = path2 = xstrdup(git_path("%s", path));
|
||||
|
||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (fd < 0)
|
||||
res = error_errno(_("could not open '%s' for writing"), path);
|
||||
else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
|
||||
res = error_errno(_("could not write to '%s'"), path);
|
||||
close(fd);
|
||||
} else if (close(fd) < 0)
|
||||
res = error_errno(_("could not close '%s'"), path);
|
||||
else {
|
||||
strbuf_reset(buffer);
|
||||
if (launch_editor(path, buffer, env) < 0)
|
||||
res = error_errno(_("could not edit '%s'"), path);
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
free(path2);
|
||||
return res;
|
||||
}
|
||||
|
|
11
strbuf.h
11
strbuf.h
|
@ -621,6 +621,17 @@ int launch_editor(const char *path, struct strbuf *buffer,
|
|||
int launch_sequence_editor(const char *path, struct strbuf *buffer,
|
||||
const char *const *env);
|
||||
|
||||
/*
|
||||
* In contrast to `launch_editor()`, this function writes out the contents
|
||||
* of the specified file first, then clears the `buffer`, then launches
|
||||
* the editor and reads back in the file contents into the `buffer`.
|
||||
* Finally, it deletes the temporary file.
|
||||
*
|
||||
* If `path` is relative, it refers to a file in the `.git` directory.
|
||||
*/
|
||||
int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
|
||||
const char *const *env);
|
||||
|
||||
void strbuf_add_lines(struct strbuf *sb,
|
||||
const char *prefix,
|
||||
const char *buf,
|
||||
|
|
|
@ -413,6 +413,36 @@ test_expect_success 'split hunk setup' '
|
|||
test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
|
||||
'
|
||||
|
||||
test_expect_success 'goto hunk' '
|
||||
test_when_finished "git reset" &&
|
||||
tr _ " " >expect <<-EOF &&
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1: -1,2 +1,3 +15
|
||||
_ 2: -2,4 +3,8 +21
|
||||
go to which hunk? @@ -1,2 +1,3 @@
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
|
||||
EOF
|
||||
test_write_lines s y g 1 | git add -p >actual &&
|
||||
tail -n 7 <actual >actual.trimmed &&
|
||||
test_cmp expect actual.trimmed
|
||||
'
|
||||
|
||||
test_expect_success 'navigate to hunk via regex' '
|
||||
test_when_finished "git reset" &&
|
||||
tr _ " " >expect <<-EOF &&
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
|
||||
EOF
|
||||
test_write_lines s y /1,2 | git add -p >actual &&
|
||||
tail -n 5 <actual >actual.trimmed &&
|
||||
test_cmp expect actual.trimmed
|
||||
'
|
||||
|
||||
test_expect_success 'split hunk "add -p (edit)"' '
|
||||
# Split, say Edit and do nothing. Then:
|
||||
#
|
||||
|
@ -442,6 +472,18 @@ test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
|
|||
! grep "^+31" actual
|
||||
'
|
||||
|
||||
test_expect_success 'split hunk with incomplete line at end' '
|
||||
git reset --hard &&
|
||||
printf "missing LF" >>test &&
|
||||
git add test &&
|
||||
test_write_lines before 10 20 30 40 50 60 70 >test &&
|
||||
git grep --cached missing &&
|
||||
test_write_lines s n y q | git add -p &&
|
||||
test_must_fail git grep --cached missing &&
|
||||
git grep before &&
|
||||
test_must_fail git grep --cached before
|
||||
'
|
||||
|
||||
test_expect_failure 'edit, adding lines to the first hunk' '
|
||||
test_write_lines 10 11 20 30 40 50 51 60 >test &&
|
||||
git reset &&
|
||||
|
|
Loading…
Reference in a new issue