built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
#include "cache.h"
|
|
|
|
#include "add-interactive.h"
|
|
|
|
#include "strbuf.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
#include "argv-array.h"
|
|
|
|
#include "pathspec.h"
|
2019-12-13 11:07:50 +03:00
|
|
|
#include "color.h"
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
#include "diff.h"
|
|
|
|
|
2019-12-13 11:07:57 +03:00
|
|
|
enum prompt_mode_type {
|
|
|
|
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *prompt_mode[] = {
|
|
|
|
N_("Stage mode change [y,n,a,d%s,?]? "),
|
|
|
|
N_("Stage deletion [y,n,a,d%s,?]? "),
|
|
|
|
N_("Stage this hunk [y,n,a,d%s,?]? ")
|
|
|
|
};
|
|
|
|
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
struct hunk_header {
|
|
|
|
unsigned long old_offset, old_count, new_offset, new_count;
|
|
|
|
/*
|
|
|
|
* Start/end offsets to the extra text after the second `@@` in the
|
|
|
|
* hunk header, e.g. the function signature. This is expected to
|
|
|
|
* include the newline.
|
|
|
|
*/
|
|
|
|
size_t extra_start, extra_end, colored_extra_start, colored_extra_end;
|
|
|
|
};
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
|
|
|
struct hunk {
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
size_t start, end, colored_start, colored_end, splittable_into;
|
2019-12-13 11:08:01 +03:00
|
|
|
ssize_t delta;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use;
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
struct hunk_header header;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
struct add_p_state {
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
struct add_i_state s;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
struct strbuf answer, buf;
|
|
|
|
|
|
|
|
/* parsed diff */
|
2019-12-13 11:07:50 +03:00
|
|
|
struct strbuf plain, colored;
|
2019-12-13 11:07:54 +03:00
|
|
|
struct file_diff {
|
|
|
|
struct hunk head;
|
|
|
|
struct hunk *hunk;
|
|
|
|
size_t hunk_nr, hunk_alloc;
|
2019-12-13 11:07:56 +03:00
|
|
|
unsigned deleted:1, mode_change:1;
|
2019-12-13 11:07:54 +03:00
|
|
|
} *file_diff;
|
|
|
|
size_t file_diff_nr;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
};
|
|
|
|
|
2019-12-13 11:07:53 +03:00
|
|
|
static void err(struct add_p_state *s, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
fputs(s->s.error_color, stderr);
|
|
|
|
vfprintf(stderr, fmt, args);
|
|
|
|
fputs(s->s.reset_color, stderr);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
static void setup_child_process(struct add_p_state *s,
|
|
|
|
struct child_process *cp, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
const char *arg;
|
|
|
|
|
|
|
|
va_start(ap, cp);
|
|
|
|
while ((arg = va_arg(ap, const char *)))
|
|
|
|
argv_array_push(&cp->args, arg);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
cp->git_cmd = 1;
|
|
|
|
argv_array_pushf(&cp->env_array,
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_range(const char **p,
|
|
|
|
unsigned long *offset, unsigned long *count)
|
|
|
|
{
|
|
|
|
char *pend;
|
|
|
|
|
|
|
|
*offset = strtoul(*p, &pend, 10);
|
|
|
|
if (pend == *p)
|
|
|
|
return -1;
|
|
|
|
if (*pend != ',') {
|
|
|
|
*count = 1;
|
|
|
|
*p = pend;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*count = strtoul(pend + 1, (char **)p, 10);
|
|
|
|
return *p == pend + 1 ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
|
|
|
|
{
|
|
|
|
struct hunk_header *header = &hunk->header;
|
|
|
|
const char *line = s->plain.buf + hunk->start, *p = line;
|
|
|
|
char *eol = memchr(p, '\n', s->plain.len - hunk->start);
|
|
|
|
|
|
|
|
if (!eol)
|
|
|
|
eol = s->plain.buf + s->plain.len;
|
|
|
|
|
|
|
|
if (!skip_prefix(p, "@@ -", &p) ||
|
|
|
|
parse_range(&p, &header->old_offset, &header->old_count) < 0 ||
|
|
|
|
!skip_prefix(p, " +", &p) ||
|
|
|
|
parse_range(&p, &header->new_offset, &header->new_count) < 0 ||
|
|
|
|
!skip_prefix(p, " @@", &p))
|
|
|
|
return error(_("could not parse hunk header '%.*s'"),
|
|
|
|
(int)(eol - line), line);
|
|
|
|
|
|
|
|
hunk->start = eol - s->plain.buf + (*eol == '\n');
|
|
|
|
header->extra_start = p - s->plain.buf;
|
|
|
|
header->extra_end = hunk->start;
|
|
|
|
|
|
|
|
if (!s->colored.len) {
|
|
|
|
header->colored_extra_start = header->colored_extra_end = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now find the extra text in the colored diff */
|
|
|
|
line = s->colored.buf + hunk->colored_start;
|
|
|
|
eol = memchr(line, '\n', s->colored.len - hunk->colored_start);
|
|
|
|
if (!eol)
|
|
|
|
eol = s->colored.buf + s->colored.len;
|
|
|
|
p = memmem(line, eol - line, "@@ -", 4);
|
|
|
|
if (!p)
|
|
|
|
return error(_("could not parse colored hunk header '%.*s'"),
|
|
|
|
(int)(eol - line), line);
|
|
|
|
p = memmem(p + 4, eol - p - 4, " @@", 3);
|
|
|
|
if (!p)
|
|
|
|
return error(_("could not parse colored hunk header '%.*s'"),
|
|
|
|
(int)(eol - line), line);
|
|
|
|
hunk->colored_start = eol - s->colored.buf + (*eol == '\n');
|
|
|
|
header->colored_extra_start = p + 3 - s->colored.buf;
|
|
|
|
header->colored_extra_end = hunk->colored_start;
|
|
|
|
|
|
|
|
return 0;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:56 +03:00
|
|
|
static int is_octal(const char *p, size_t len)
|
|
|
|
{
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (len--)
|
|
|
|
if (*p < '0' || *(p++) > '7')
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|
|
|
{
|
2019-12-13 11:07:50 +03:00
|
|
|
struct argv_array args = ARGV_ARRAY_INIT;
|
|
|
|
struct strbuf *plain = &s->plain, *colored = NULL;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
|
2019-12-13 11:07:54 +03:00
|
|
|
size_t file_diff_alloc = 0, i, color_arg_index;
|
|
|
|
struct file_diff *file_diff = NULL;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
struct hunk *hunk = NULL;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
|
2019-12-13 11:07:50 +03:00
|
|
|
argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
|
|
|
|
color_arg_index = args.argc - 2;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
for (i = 0; i < ps->nr; i++)
|
2019-12-13 11:07:50 +03:00
|
|
|
argv_array_push(&args, ps->items[i].original);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
2019-12-13 11:07:50 +03:00
|
|
|
setup_child_process(s, &cp, NULL);
|
|
|
|
cp.argv = args.argv;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
res = capture_command(&cp, plain, 0);
|
2019-12-13 11:07:50 +03:00
|
|
|
if (res) {
|
|
|
|
argv_array_clear(&args);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
return error(_("could not parse diff"));
|
2019-12-13 11:07:50 +03:00
|
|
|
}
|
|
|
|
if (!plain->len) {
|
|
|
|
argv_array_clear(&args);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
return 0;
|
2019-12-13 11:07:50 +03:00
|
|
|
}
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
strbuf_complete_line(plain);
|
|
|
|
|
2019-12-13 11:07:50 +03:00
|
|
|
if (want_color_fd(1, -1)) {
|
|
|
|
struct child_process colored_cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
setup_child_process(s, &colored_cp, NULL);
|
|
|
|
xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
|
|
|
|
colored_cp.argv = args.argv;
|
|
|
|
colored = &s->colored;
|
|
|
|
res = capture_command(&colored_cp, colored, 0);
|
|
|
|
argv_array_clear(&args);
|
|
|
|
if (res)
|
|
|
|
return error(_("could not parse colored diff"));
|
|
|
|
strbuf_complete_line(colored);
|
|
|
|
colored_p = colored->buf;
|
|
|
|
colored_pend = colored_p + colored->len;
|
|
|
|
}
|
|
|
|
argv_array_clear(&args);
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
/* parse files and hunks */
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
p = plain->buf;
|
|
|
|
pend = p + plain->len;
|
|
|
|
while (p != pend) {
|
|
|
|
char *eol = memchr(p, '\n', pend - p);
|
2019-12-13 11:07:56 +03:00
|
|
|
const char *deleted = NULL, *mode_change = NULL;
|
2019-12-13 11:07:55 +03:00
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
if (!eol)
|
|
|
|
eol = pend;
|
|
|
|
|
|
|
|
if (starts_with(p, "diff ")) {
|
2019-12-13 11:07:54 +03:00
|
|
|
s->file_diff_nr++;
|
|
|
|
ALLOC_GROW(s->file_diff, s->file_diff_nr,
|
|
|
|
file_diff_alloc);
|
|
|
|
file_diff = s->file_diff + s->file_diff_nr - 1;
|
|
|
|
memset(file_diff, 0, sizeof(*file_diff));
|
|
|
|
hunk = &file_diff->head;
|
|
|
|
hunk->start = p - plain->buf;
|
|
|
|
if (colored_p)
|
|
|
|
hunk->colored_start = colored_p - colored->buf;
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
marker = '\0';
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
} else if (p == plain->buf)
|
|
|
|
BUG("diff starts with unexpected line:\n"
|
|
|
|
"%.*s\n", (int)(eol - p), p);
|
2019-12-13 11:07:55 +03:00
|
|
|
else if (file_diff->deleted)
|
|
|
|
; /* keep the rest of the file in a single "hunk" */
|
|
|
|
else if (starts_with(p, "@@ ") ||
|
|
|
|
(hunk == &file_diff->head &&
|
|
|
|
skip_prefix(p, "deleted file", &deleted))) {
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
if (marker == '-' || marker == '+')
|
|
|
|
/*
|
|
|
|
* Should not happen; previous hunk did not end
|
|
|
|
* in a context line? Handle it anyway.
|
|
|
|
*/
|
|
|
|
hunk->splittable_into++;
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
file_diff->hunk_nr++;
|
|
|
|
ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
|
|
|
|
file_diff->hunk_alloc);
|
|
|
|
hunk = file_diff->hunk + file_diff->hunk_nr - 1;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
memset(hunk, 0, sizeof(*hunk));
|
|
|
|
|
|
|
|
hunk->start = p - plain->buf;
|
2019-12-13 11:07:50 +03:00
|
|
|
if (colored)
|
|
|
|
hunk->colored_start = colored_p - colored->buf;
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
|
2019-12-13 11:07:55 +03:00
|
|
|
if (deleted)
|
|
|
|
file_diff->deleted = 1;
|
|
|
|
else if (parse_hunk_header(s, hunk) < 0)
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
return -1;
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Start counting into how many hunks this one can be
|
|
|
|
* split
|
|
|
|
*/
|
|
|
|
marker = *p;
|
2019-12-13 11:07:56 +03:00
|
|
|
} else if (hunk == &file_diff->head &&
|
|
|
|
skip_prefix(p, "old mode ", &mode_change) &&
|
|
|
|
is_octal(mode_change, eol - mode_change)) {
|
|
|
|
if (file_diff->mode_change)
|
|
|
|
BUG("double mode change?\n\n%.*s",
|
|
|
|
(int)(eol - plain->buf), plain->buf);
|
|
|
|
if (file_diff->hunk_nr++)
|
|
|
|
BUG("mode change in the middle?\n\n%.*s",
|
|
|
|
(int)(eol - plain->buf), plain->buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do *not* change `hunk`: the mode change pseudo-hunk
|
|
|
|
* is _part of_ the header "hunk".
|
|
|
|
*/
|
|
|
|
file_diff->mode_change = 1;
|
|
|
|
ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
|
|
|
|
file_diff->hunk_alloc);
|
|
|
|
memset(file_diff->hunk, 0, sizeof(struct hunk));
|
|
|
|
file_diff->hunk->start = p - plain->buf;
|
|
|
|
if (colored_p)
|
|
|
|
file_diff->hunk->colored_start =
|
|
|
|
colored_p - colored->buf;
|
|
|
|
} else if (hunk == &file_diff->head &&
|
|
|
|
skip_prefix(p, "new mode ", &mode_change) &&
|
|
|
|
is_octal(mode_change, eol - mode_change)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extend the "mode change" pseudo-hunk to include also
|
|
|
|
* the "new mode" line.
|
|
|
|
*/
|
|
|
|
if (!file_diff->mode_change)
|
|
|
|
BUG("'new mode' without 'old mode'?\n\n%.*s",
|
|
|
|
(int)(eol - plain->buf), plain->buf);
|
|
|
|
if (file_diff->hunk_nr != 1)
|
|
|
|
BUG("mode change in the middle?\n\n%.*s",
|
|
|
|
(int)(eol - plain->buf), plain->buf);
|
|
|
|
if (p - plain->buf != file_diff->hunk->end)
|
|
|
|
BUG("'new mode' does not immediately follow "
|
|
|
|
"'old mode'?\n\n%.*s",
|
|
|
|
(int)(eol - plain->buf), plain->buf);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:56 +03:00
|
|
|
if (file_diff->deleted && file_diff->mode_change)
|
|
|
|
BUG("diff contains delete *and* a mode change?!?\n%.*s",
|
|
|
|
(int)(eol - (plain->buf + file_diff->head.start)),
|
|
|
|
plain->buf + file_diff->head.start);
|
|
|
|
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
if ((marker == '-' || marker == '+') && *p == ' ')
|
|
|
|
hunk->splittable_into++;
|
|
|
|
if (marker && *p != '\\')
|
|
|
|
marker = *p;
|
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
p = eol == pend ? pend : eol + 1;
|
|
|
|
hunk->end = p - plain->buf;
|
2019-12-13 11:07:50 +03:00
|
|
|
|
|
|
|
if (colored) {
|
|
|
|
char *colored_eol = memchr(colored_p, '\n',
|
|
|
|
colored_pend - colored_p);
|
|
|
|
if (colored_eol)
|
|
|
|
colored_p = colored_eol + 1;
|
|
|
|
else
|
|
|
|
colored_p = colored_pend;
|
|
|
|
|
|
|
|
hunk->colored_end = colored_p - colored->buf;
|
|
|
|
}
|
2019-12-13 11:07:56 +03:00
|
|
|
|
|
|
|
if (mode_change) {
|
|
|
|
if (file_diff->hunk_nr != 1)
|
|
|
|
BUG("mode change in hunk #%d???",
|
|
|
|
(int)file_diff->hunk_nr);
|
|
|
|
/* Adjust the end of the "mode change" pseudo-hunk */
|
|
|
|
file_diff->hunk->end = hunk->end;
|
|
|
|
if (colored)
|
|
|
|
file_diff->hunk->colored_end = hunk->colored_end;
|
|
|
|
}
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
}
|
|
|
|
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
if (marker == '-' || marker == '+')
|
|
|
|
/*
|
|
|
|
* Last hunk ended in non-context line (i.e. it appended lines
|
|
|
|
* to the file, so there are no trailing context lines).
|
|
|
|
*/
|
|
|
|
hunk->splittable_into++;
|
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
static size_t find_next_line(struct strbuf *sb, size_t offset)
|
|
|
|
{
|
|
|
|
char *eol;
|
|
|
|
|
|
|
|
if (offset >= sb->len)
|
|
|
|
BUG("looking for next line beyond buffer (%d >= %d)\n%s",
|
|
|
|
(int)offset, (int)sb->len, sb->buf);
|
|
|
|
|
|
|
|
eol = memchr(sb->buf + offset, '\n', sb->len - offset);
|
|
|
|
if (!eol)
|
|
|
|
return sb->len;
|
|
|
|
return eol - sb->buf + 1;
|
|
|
|
}
|
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
static void render_hunk(struct add_p_state *s, struct hunk *hunk,
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
ssize_t delta, int colored, struct strbuf *out)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
{
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
struct hunk_header *header = &hunk->header;
|
|
|
|
|
|
|
|
if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) {
|
|
|
|
/*
|
|
|
|
* Generate the hunk header dynamically, except for special
|
|
|
|
* hunks (such as the diff header).
|
|
|
|
*/
|
|
|
|
const char *p;
|
|
|
|
size_t len;
|
|
|
|
unsigned long old_offset = header->old_offset;
|
|
|
|
unsigned long new_offset = header->new_offset;
|
|
|
|
|
|
|
|
if (!colored) {
|
|
|
|
p = s->plain.buf + header->extra_start;
|
|
|
|
len = header->extra_end - header->extra_start;
|
|
|
|
} else {
|
|
|
|
strbuf_addstr(out, s->s.fraginfo_color);
|
|
|
|
p = s->colored.buf + header->colored_extra_start;
|
|
|
|
len = header->colored_extra_end
|
|
|
|
- header->colored_extra_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_offset += delta;
|
|
|
|
|
|
|
|
strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
|
|
|
|
old_offset, header->old_count,
|
|
|
|
new_offset, header->new_count);
|
|
|
|
if (len)
|
|
|
|
strbuf_add(out, p, len);
|
|
|
|
else if (colored)
|
|
|
|
strbuf_addf(out, "%s\n", GIT_COLOR_RESET);
|
|
|
|
else
|
|
|
|
strbuf_addch(out, '\n');
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:50 +03:00
|
|
|
if (colored)
|
|
|
|
strbuf_add(out, s->colored.buf + hunk->colored_start,
|
|
|
|
hunk->colored_end - hunk->colored_start);
|
|
|
|
else
|
|
|
|
strbuf_add(out, s->plain.buf + hunk->start,
|
|
|
|
hunk->end - hunk->start);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:56 +03:00
|
|
|
static void render_diff_header(struct add_p_state *s,
|
|
|
|
struct file_diff *file_diff, int colored,
|
|
|
|
struct strbuf *out)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If there was a mode change, the first hunk is a pseudo hunk that
|
|
|
|
* corresponds to the mode line in the header. If the user did not want
|
|
|
|
* to stage that "hunk", we actually have to cut it out from the header.
|
|
|
|
*/
|
|
|
|
int skip_mode_change =
|
|
|
|
file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
|
|
|
|
struct hunk *head = &file_diff->head, *first = file_diff->hunk;
|
|
|
|
|
|
|
|
if (!skip_mode_change) {
|
|
|
|
render_hunk(s, head, 0, colored, out);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (colored) {
|
|
|
|
const char *p = s->colored.buf;
|
|
|
|
|
|
|
|
strbuf_add(out, p + head->colored_start,
|
|
|
|
first->colored_start - head->colored_start);
|
|
|
|
strbuf_add(out, p + first->colored_end,
|
|
|
|
head->colored_end - first->colored_end);
|
|
|
|
} else {
|
|
|
|
const char *p = s->plain.buf;
|
|
|
|
|
|
|
|
strbuf_add(out, p + head->start, first->start - head->start);
|
|
|
|
strbuf_add(out, p + first->end, head->end - first->end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:59 +03:00
|
|
|
/* Coalesce hunks again that were split */
|
|
|
|
static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
|
2019-12-13 11:08:01 +03:00
|
|
|
size_t *hunk_index, int use_all, struct hunk *merged)
|
2019-12-13 11:07:59 +03:00
|
|
|
{
|
2019-12-13 11:08:01 +03:00
|
|
|
size_t i = *hunk_index, delta;
|
2019-12-13 11:07:59 +03:00
|
|
|
struct hunk *hunk = file_diff->hunk + i;
|
|
|
|
/* `header` corresponds to the merged hunk */
|
|
|
|
struct hunk_header *header = &merged->header, *next;
|
|
|
|
|
2019-12-13 11:08:01 +03:00
|
|
|
if (!use_all && hunk->use != USE_HUNK)
|
2019-12-13 11:07:59 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
*merged = *hunk;
|
|
|
|
/* We simply skip the colored part (if any) when merging hunks */
|
|
|
|
merged->colored_start = merged->colored_end = 0;
|
|
|
|
|
|
|
|
for (; i + 1 < file_diff->hunk_nr; i++) {
|
|
|
|
hunk++;
|
|
|
|
next = &hunk->header;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop merging hunks when:
|
|
|
|
*
|
|
|
|
* - the hunk is not selected for use, or
|
|
|
|
* - the hunk does not overlap with the already-merged hunk(s)
|
|
|
|
*/
|
2019-12-13 11:08:01 +03:00
|
|
|
if ((!use_all && hunk->use != USE_HUNK) ||
|
|
|
|
header->new_offset >= next->new_offset + merged->delta ||
|
|
|
|
header->new_offset + header->new_count
|
|
|
|
< next->new_offset + merged->delta)
|
2019-12-13 11:07:59 +03:00
|
|
|
break;
|
|
|
|
|
2019-12-13 11:08:01 +03:00
|
|
|
/*
|
|
|
|
* If the hunks were not edited, and overlap, we can simply
|
|
|
|
* extend the line range.
|
|
|
|
*/
|
|
|
|
if (merged->start < hunk->start && merged->end > hunk->start) {
|
|
|
|
merged->end = hunk->end;
|
|
|
|
merged->colored_end = hunk->colored_end;
|
|
|
|
delta = 0;
|
|
|
|
} else {
|
|
|
|
const char *plain = s->plain.buf;
|
|
|
|
size_t overlapping_line_count = header->new_offset
|
|
|
|
+ header->new_count - merged->delta
|
|
|
|
- next->new_offset;
|
|
|
|
size_t overlap_end = hunk->start;
|
|
|
|
size_t overlap_start = overlap_end;
|
|
|
|
size_t overlap_next, len, j;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* One of the hunks was edited: the modified hunk was
|
|
|
|
* appended to the strbuf `s->plain`.
|
|
|
|
*
|
|
|
|
* Let's ensure that at least the last context line of
|
|
|
|
* the first hunk overlaps with the corresponding line
|
|
|
|
* of the second hunk, and then merge.
|
|
|
|
*/
|
|
|
|
for (j = 0; j < overlapping_line_count; j++) {
|
|
|
|
overlap_next = find_next_line(&s->plain,
|
|
|
|
overlap_end);
|
|
|
|
|
|
|
|
if (overlap_next > hunk->end)
|
|
|
|
BUG("failed to find %d context lines "
|
|
|
|
"in:\n%.*s",
|
|
|
|
(int)overlapping_line_count,
|
|
|
|
(int)(hunk->end - hunk->start),
|
|
|
|
plain + hunk->start);
|
|
|
|
|
|
|
|
if (plain[overlap_end] != ' ')
|
|
|
|
return error(_("expected context line "
|
|
|
|
"#%d in\n%.*s"),
|
|
|
|
(int)(j + 1),
|
|
|
|
(int)(hunk->end
|
|
|
|
- hunk->start),
|
|
|
|
plain + hunk->start);
|
|
|
|
|
|
|
|
overlap_start = overlap_end;
|
|
|
|
overlap_end = overlap_next;
|
|
|
|
}
|
|
|
|
len = overlap_end - overlap_start;
|
|
|
|
|
|
|
|
if (len > merged->end - merged->start ||
|
|
|
|
memcmp(plain + merged->end - len,
|
|
|
|
plain + overlap_start, len))
|
|
|
|
return error(_("hunks do not overlap:\n%.*s\n"
|
|
|
|
"\tdoes not end with:\n%.*s"),
|
|
|
|
(int)(merged->end - merged->start),
|
|
|
|
plain + merged->start,
|
|
|
|
(int)len, plain + overlap_start);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since the start-end ranges are not adjacent, we
|
|
|
|
* cannot simply take the union of the ranges. To
|
|
|
|
* address that, we temporarily append the union of the
|
|
|
|
* lines to the `plain` strbuf.
|
|
|
|
*/
|
|
|
|
if (merged->end != s->plain.len) {
|
|
|
|
size_t start = s->plain.len;
|
|
|
|
|
|
|
|
strbuf_add(&s->plain, plain + merged->start,
|
|
|
|
merged->end - merged->start);
|
|
|
|
plain = s->plain.buf;
|
|
|
|
merged->start = start;
|
|
|
|
merged->end = s->plain.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_add(&s->plain,
|
|
|
|
plain + overlap_end,
|
|
|
|
hunk->end - overlap_end);
|
|
|
|
merged->end = s->plain.len;
|
|
|
|
merged->splittable_into += hunk->splittable_into;
|
|
|
|
delta = merged->delta;
|
|
|
|
merged->delta += hunk->delta;
|
|
|
|
}
|
2019-12-13 11:07:59 +03:00
|
|
|
|
|
|
|
header->old_count = next->old_offset + next->old_count
|
|
|
|
- header->old_offset;
|
2019-12-13 11:08:01 +03:00
|
|
|
header->new_count = next->new_offset + delta
|
|
|
|
+ next->new_count - header->new_offset;
|
2019-12-13 11:07:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i == *hunk_index)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*hunk_index = i;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
static void reassemble_patch(struct add_p_state *s,
|
2019-12-13 11:08:01 +03:00
|
|
|
struct file_diff *file_diff, int use_all,
|
|
|
|
struct strbuf *out)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
{
|
|
|
|
struct hunk *hunk;
|
2019-12-13 11:08:01 +03:00
|
|
|
size_t save_len = s->plain.len, i;
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
ssize_t delta = 0;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
2019-12-13 11:07:56 +03:00
|
|
|
render_diff_header(s, file_diff, 0, out);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
2019-12-13 11:07:56 +03:00
|
|
|
for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
|
2019-12-13 11:07:59 +03:00
|
|
|
struct hunk merged = { 0 };
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
hunk = file_diff->hunk + i;
|
2019-12-13 11:08:01 +03:00
|
|
|
if (!use_all && hunk->use != USE_HUNK)
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
delta += hunk->header.old_count
|
|
|
|
- hunk->header.new_count;
|
2019-12-13 11:07:59 +03:00
|
|
|
else {
|
|
|
|
/* merge overlapping hunks into a temporary hunk */
|
2019-12-13 11:08:01 +03:00
|
|
|
if (merge_hunks(s, file_diff, &i, use_all, &merged))
|
2019-12-13 11:07:59 +03:00
|
|
|
hunk = &merged;
|
|
|
|
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
render_hunk(s, hunk, delta, 0, out);
|
2019-12-13 11:08:01 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In case `merge_hunks()` used `plain` as a scratch
|
|
|
|
* pad (this happens when an edited hunk had to be
|
|
|
|
* coalesced with another hunk).
|
|
|
|
*/
|
|
|
|
strbuf_setlen(&s->plain, save_len);
|
|
|
|
|
|
|
|
delta += hunk->delta;
|
2019-12-13 11:07:59 +03:00
|
|
|
}
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
|
|
|
|
size_t hunk_index)
|
|
|
|
{
|
|
|
|
int colored = !!s->colored.len, first = 1;
|
|
|
|
struct hunk *hunk = file_diff->hunk + hunk_index;
|
|
|
|
size_t splittable_into;
|
|
|
|
size_t end, colored_end, current, colored_current = 0, context_line_count;
|
|
|
|
struct hunk_header remaining, *header;
|
|
|
|
char marker, ch;
|
|
|
|
|
|
|
|
if (hunk_index >= file_diff->hunk_nr)
|
|
|
|
BUG("invalid hunk index: %d (must be >= 0 and < %d)",
|
|
|
|
(int)hunk_index, (int)file_diff->hunk_nr);
|
|
|
|
|
|
|
|
if (hunk->splittable_into < 2)
|
|
|
|
return 0;
|
|
|
|
splittable_into = hunk->splittable_into;
|
|
|
|
|
|
|
|
end = hunk->end;
|
|
|
|
colored_end = hunk->colored_end;
|
|
|
|
|
|
|
|
remaining = hunk->header;
|
|
|
|
|
|
|
|
file_diff->hunk_nr += splittable_into - 1;
|
|
|
|
ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
|
|
|
|
if (hunk_index + splittable_into < file_diff->hunk_nr)
|
|
|
|
memmove(file_diff->hunk + hunk_index + splittable_into,
|
|
|
|
file_diff->hunk + hunk_index + 1,
|
|
|
|
(file_diff->hunk_nr - hunk_index - splittable_into)
|
|
|
|
* sizeof(*hunk));
|
|
|
|
hunk = file_diff->hunk + hunk_index;
|
|
|
|
hunk->splittable_into = 1;
|
|
|
|
memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
|
|
|
|
|
|
|
|
header = &hunk->header;
|
|
|
|
header->old_count = header->new_count = 0;
|
|
|
|
|
|
|
|
current = hunk->start;
|
|
|
|
if (colored)
|
|
|
|
colored_current = hunk->colored_start;
|
|
|
|
marker = '\0';
|
|
|
|
context_line_count = 0;
|
|
|
|
|
|
|
|
while (splittable_into > 1) {
|
|
|
|
ch = s->plain.buf[current];
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
BUG("buffer overrun while splitting hunks");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this the first context line after a chain of +/- lines?
|
|
|
|
* Then record the start of the next split hunk.
|
|
|
|
*/
|
|
|
|
if ((marker == '-' || marker == '+') && ch == ' ') {
|
|
|
|
first = 0;
|
|
|
|
hunk[1].start = current;
|
|
|
|
if (colored)
|
|
|
|
hunk[1].colored_start = colored_current;
|
|
|
|
context_line_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Was the previous line a +/- one? Alternatively, is this the
|
|
|
|
* first line (and not a +/- one)?
|
|
|
|
*
|
|
|
|
* Then just increment the appropriate counter and continue
|
|
|
|
* with the next line.
|
|
|
|
*/
|
|
|
|
if (marker != ' ' || (ch != '-' && ch != '+')) {
|
|
|
|
next_hunk_line:
|
|
|
|
/* Comment lines are attached to the previous line */
|
|
|
|
if (ch == '\\')
|
|
|
|
ch = marker ? marker : ' ';
|
|
|
|
|
|
|
|
/* current hunk not done yet */
|
|
|
|
if (ch == ' ')
|
|
|
|
context_line_count++;
|
|
|
|
else if (ch == '-')
|
|
|
|
header->old_count++;
|
|
|
|
else if (ch == '+')
|
|
|
|
header->new_count++;
|
|
|
|
else
|
|
|
|
BUG("unhandled diff marker: '%c'", ch);
|
|
|
|
marker = ch;
|
|
|
|
current = find_next_line(&s->plain, current);
|
|
|
|
if (colored)
|
|
|
|
colored_current =
|
|
|
|
find_next_line(&s->colored,
|
|
|
|
colored_current);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We got us the start of a new hunk!
|
|
|
|
*
|
|
|
|
* This is a context line, so it is shared with the previous
|
|
|
|
* hunk, if any.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (first) {
|
|
|
|
if (header->old_count || header->new_count)
|
|
|
|
BUG("counts are off: %d/%d",
|
|
|
|
(int)header->old_count,
|
|
|
|
(int)header->new_count);
|
|
|
|
|
|
|
|
header->old_count = context_line_count;
|
|
|
|
header->new_count = context_line_count;
|
|
|
|
context_line_count = 0;
|
|
|
|
first = 0;
|
|
|
|
goto next_hunk_line;
|
|
|
|
}
|
|
|
|
|
|
|
|
remaining.old_offset += header->old_count;
|
|
|
|
remaining.old_count -= header->old_count;
|
|
|
|
remaining.new_offset += header->new_count;
|
|
|
|
remaining.new_count -= header->new_count;
|
|
|
|
|
|
|
|
/* initialize next hunk header's offsets */
|
|
|
|
hunk[1].header.old_offset =
|
|
|
|
header->old_offset + header->old_count;
|
|
|
|
hunk[1].header.new_offset =
|
|
|
|
header->new_offset + header->new_count;
|
|
|
|
|
|
|
|
/* add one split hunk */
|
|
|
|
header->old_count += context_line_count;
|
|
|
|
header->new_count += context_line_count;
|
|
|
|
|
|
|
|
hunk->end = current;
|
|
|
|
if (colored)
|
|
|
|
hunk->colored_end = colored_current;
|
|
|
|
|
|
|
|
hunk++;
|
|
|
|
hunk->splittable_into = 1;
|
|
|
|
hunk->use = hunk[-1].use;
|
|
|
|
header = &hunk->header;
|
|
|
|
|
|
|
|
header->old_count = header->new_count = context_line_count;
|
|
|
|
context_line_count = 0;
|
|
|
|
|
|
|
|
splittable_into--;
|
|
|
|
marker = ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* last hunk simply gets the rest */
|
|
|
|
if (header->old_offset != remaining.old_offset)
|
|
|
|
BUG("miscounted old_offset: %lu != %lu",
|
|
|
|
header->old_offset, remaining.old_offset);
|
|
|
|
if (header->new_offset != remaining.new_offset)
|
|
|
|
BUG("miscounted new_offset: %lu != %lu",
|
|
|
|
header->new_offset, remaining.new_offset);
|
|
|
|
header->old_count = remaining.old_count;
|
|
|
|
header->new_count = remaining.new_count;
|
|
|
|
hunk->end = end;
|
|
|
|
if (colored)
|
|
|
|
hunk->colored_end = colored_end;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:08:01 +03:00
|
|
|
static void recolor_hunk(struct add_p_state *s, struct hunk *hunk)
|
|
|
|
{
|
|
|
|
const char *plain = s->plain.buf;
|
|
|
|
size_t current, eol, next;
|
|
|
|
|
|
|
|
if (!s->colored.len)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hunk->colored_start = s->colored.len;
|
|
|
|
for (current = hunk->start; current < hunk->end; ) {
|
|
|
|
for (eol = current; eol < hunk->end; eol++)
|
|
|
|
if (plain[eol] == '\n')
|
|
|
|
break;
|
|
|
|
next = eol + (eol < hunk->end);
|
|
|
|
if (eol > current && plain[eol - 1] == '\r')
|
|
|
|
eol--;
|
|
|
|
|
|
|
|
strbuf_addstr(&s->colored,
|
|
|
|
plain[current] == '-' ?
|
|
|
|
s->s.file_old_color :
|
|
|
|
plain[current] == '+' ?
|
|
|
|
s->s.file_new_color :
|
|
|
|
s->s.context_color);
|
|
|
|
strbuf_add(&s->colored, plain + current, eol - current);
|
|
|
|
strbuf_addstr(&s->colored, GIT_COLOR_RESET);
|
|
|
|
if (next > eol)
|
|
|
|
strbuf_add(&s->colored, plain + eol, next - eol);
|
|
|
|
current = next;
|
|
|
|
}
|
|
|
|
hunk->colored_end = s->colored.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
strbuf_reset(&s->buf);
|
|
|
|
strbuf_commented_addf(&s->buf, _("Manual hunk edit mode -- see bottom for "
|
|
|
|
"a quick guide.\n"));
|
|
|
|
render_hunk(s, hunk, 0, 0, &s->buf);
|
|
|
|
strbuf_commented_addf(&s->buf,
|
|
|
|
_("---\n"
|
|
|
|
"To remove '%c' lines, make them ' ' lines "
|
|
|
|
"(context).\n"
|
|
|
|
"To remove '%c' lines, delete them.\n"
|
|
|
|
"Lines starting with %c will be removed.\n"),
|
|
|
|
'-', '+', comment_line_char);
|
|
|
|
strbuf_commented_addf(&s->buf,
|
|
|
|
_("If the patch applies cleanly, the edited hunk "
|
|
|
|
"will immediately be\n"
|
|
|
|
"marked for staging.\n"));
|
|
|
|
/*
|
|
|
|
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
|
|
|
|
* messages.
|
|
|
|
*/
|
|
|
|
strbuf_commented_addf(&s->buf,
|
|
|
|
_("If it does not apply cleanly, you will be "
|
|
|
|
"given an opportunity to\n"
|
|
|
|
"edit again. If all lines of the hunk are "
|
|
|
|
"removed, then the edit is\n"
|
|
|
|
"aborted and the hunk is left unchanged.\n"));
|
|
|
|
|
|
|
|
if (strbuf_edit_interactively(&s->buf, "addp-hunk-edit.diff", NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* strip out commented lines */
|
|
|
|
hunk->start = s->plain.len;
|
|
|
|
for (i = 0; i < s->buf.len; ) {
|
|
|
|
size_t next = find_next_line(&s->buf, i);
|
|
|
|
|
|
|
|
if (s->buf.buf[i] != comment_line_char)
|
|
|
|
strbuf_add(&s->plain, s->buf.buf + i, next - i);
|
|
|
|
i = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
hunk->end = s->plain.len;
|
|
|
|
if (hunk->end == hunk->start)
|
|
|
|
/* The user aborted editing by deleting everything */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
recolor_hunk(s, hunk);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the hunk header is intact, parse it, otherwise simply use the
|
|
|
|
* hunk header prior to editing (which will adjust `hunk->start` to
|
|
|
|
* skip the hunk header).
|
|
|
|
*/
|
|
|
|
if (s->plain.buf[hunk->start] == '@' &&
|
|
|
|
parse_hunk_header(s, hunk) < 0)
|
|
|
|
return error(_("could not parse hunk header"));
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t recount_edited_hunk(struct add_p_state *s, struct hunk *hunk,
|
|
|
|
size_t orig_old_count, size_t orig_new_count)
|
|
|
|
{
|
|
|
|
struct hunk_header *header = &hunk->header;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
header->old_count = header->new_count = 0;
|
|
|
|
for (i = hunk->start; i < hunk->end; ) {
|
|
|
|
switch (s->plain.buf[i]) {
|
|
|
|
case '-':
|
|
|
|
header->old_count++;
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
header->new_count++;
|
|
|
|
break;
|
|
|
|
case ' ': case '\r': case '\n':
|
|
|
|
header->old_count++;
|
|
|
|
header->new_count++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = find_next_line(&s->plain, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return orig_old_count - orig_new_count
|
|
|
|
- header->old_count + header->new_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int run_apply_check(struct add_p_state *s,
|
|
|
|
struct file_diff *file_diff)
|
|
|
|
{
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
strbuf_reset(&s->buf);
|
|
|
|
reassemble_patch(s, file_diff, 1, &s->buf);
|
|
|
|
|
|
|
|
setup_child_process(s, &cp,
|
|
|
|
"apply", "--cached", "--check", NULL);
|
|
|
|
if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
|
|
|
|
return error(_("'git apply --cached' failed"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int prompt_yesno(struct add_p_state *s, const char *prompt)
|
|
|
|
{
|
|
|
|
for (;;) {
|
|
|
|
color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
|
|
|
|
fflush(stdout);
|
|
|
|
if (strbuf_getline(&s->answer, stdin) == EOF)
|
|
|
|
return -1;
|
|
|
|
strbuf_trim_trailing_newline(&s->answer);
|
|
|
|
switch (tolower(s->answer.buf[0])) {
|
|
|
|
case 'n': return 0;
|
|
|
|
case 'y': return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int edit_hunk_loop(struct add_p_state *s,
|
|
|
|
struct file_diff *file_diff, struct hunk *hunk)
|
|
|
|
{
|
|
|
|
size_t plain_len = s->plain.len, colored_len = s->colored.len;
|
|
|
|
struct hunk backup;
|
|
|
|
|
|
|
|
backup = *hunk;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int res = edit_hunk_manually(s, hunk);
|
|
|
|
if (res == 0) {
|
|
|
|
/* abandonded */
|
|
|
|
*hunk = backup;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res > 0) {
|
|
|
|
hunk->delta +=
|
|
|
|
recount_edited_hunk(s, hunk,
|
|
|
|
backup.header.old_count,
|
|
|
|
backup.header.new_count);
|
|
|
|
if (!run_apply_check(s, file_diff))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drop edits (they were appended to s->plain) */
|
|
|
|
strbuf_setlen(&s->plain, plain_len);
|
|
|
|
strbuf_setlen(&s->colored, colored_len);
|
|
|
|
*hunk = backup;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TRANSLATORS: do not translate [y/n]
|
|
|
|
* The program will only accept that input at this point.
|
|
|
|
* Consider translating (saying "no" discards!) as
|
|
|
|
* (saying "n" for "no" discards!) if the translation
|
|
|
|
* of the word "no" does not start with n.
|
|
|
|
*/
|
|
|
|
res = prompt_yesno(s, _("Your edited hunk does not apply. "
|
|
|
|
"Edit again (saying \"no\" discards!) "
|
|
|
|
"[y/n]? "));
|
|
|
|
if (res < 1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:08:02 +03:00
|
|
|
#define SUMMARY_HEADER_WIDTH 20
|
|
|
|
#define SUMMARY_LINE_WIDTH 80
|
|
|
|
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
|
|
|
|
struct strbuf *out)
|
|
|
|
{
|
|
|
|
struct hunk_header *header = &hunk->header;
|
|
|
|
struct strbuf *plain = &s->plain;
|
|
|
|
size_t len = out->len, i;
|
|
|
|
|
|
|
|
strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
|
|
|
|
header->old_offset, header->old_count,
|
|
|
|
header->new_offset, header->new_count);
|
|
|
|
if (out->len - len < SUMMARY_HEADER_WIDTH)
|
|
|
|
strbuf_addchars(out, ' ',
|
|
|
|
SUMMARY_HEADER_WIDTH + len - out->len);
|
|
|
|
for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
|
|
|
|
if (plain->buf[i] != ' ')
|
|
|
|
break;
|
|
|
|
if (i < hunk->end)
|
|
|
|
strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
|
|
|
|
if (out->len - len > SUMMARY_LINE_WIDTH)
|
|
|
|
strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
|
|
|
|
strbuf_complete_line(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DISPLAY_HUNKS_LINES 20
|
|
|
|
static size_t display_hunks(struct add_p_state *s,
|
|
|
|
struct file_diff *file_diff, size_t start_index)
|
|
|
|
{
|
|
|
|
size_t end_index = start_index + DISPLAY_HUNKS_LINES;
|
|
|
|
|
|
|
|
if (end_index > file_diff->hunk_nr)
|
|
|
|
end_index = file_diff->hunk_nr;
|
|
|
|
|
|
|
|
while (start_index < end_index) {
|
|
|
|
struct hunk *hunk = file_diff->hunk + start_index++;
|
|
|
|
|
|
|
|
strbuf_reset(&s->buf);
|
|
|
|
strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
|
|
|
|
: hunk->use == SKIP_HUNK ? '-' : ' ',
|
|
|
|
(int)start_index);
|
|
|
|
summarize_hunk(s, hunk, &s->buf);
|
|
|
|
fputs(s->buf.buf, stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
return end_index;
|
|
|
|
}
|
|
|
|
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
static const char help_patch_text[] =
|
|
|
|
N_("y - stage this hunk\n"
|
|
|
|
"n - do not stage this hunk\n"
|
|
|
|
"a - stage this and all the remaining hunks\n"
|
|
|
|
"d - do not stage this hunk nor any of the remaining hunks\n"
|
|
|
|
"j - leave this hunk undecided, see next undecided hunk\n"
|
|
|
|
"J - leave this hunk undecided, see next hunk\n"
|
|
|
|
"k - leave this hunk undecided, see previous undecided hunk\n"
|
|
|
|
"K - leave this hunk undecided, see previous hunk\n"
|
2019-12-13 11:08:02 +03:00
|
|
|
"g - select a hunk to go to\n"
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
"s - split the current hunk into smaller hunks\n"
|
2019-12-13 11:08:01 +03:00
|
|
|
"e - manually edit the current hunk\n"
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
"? - print help\n");
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
static int patch_update_file(struct add_p_state *s,
|
|
|
|
struct file_diff *file_diff)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
{
|
|
|
|
size_t hunk_index = 0;
|
|
|
|
ssize_t i, undecided_previous, undecided_next;
|
|
|
|
struct hunk *hunk;
|
|
|
|
char ch;
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
2019-12-13 11:07:50 +03:00
|
|
|
int colored = !!s->colored.len;
|
2019-12-13 11:07:57 +03:00
|
|
|
enum prompt_mode_type prompt_mode_type;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
if (!file_diff->hunk_nr)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
strbuf_reset(&s->buf);
|
2019-12-13 11:07:56 +03:00
|
|
|
render_diff_header(s, file_diff, colored, &s->buf);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
fputs(s->buf.buf, stdout);
|
|
|
|
for (;;) {
|
2019-12-13 11:07:54 +03:00
|
|
|
if (hunk_index >= file_diff->hunk_nr)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
hunk_index = 0;
|
2019-12-13 11:07:54 +03:00
|
|
|
hunk = file_diff->hunk + hunk_index;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
|
|
|
undecided_previous = -1;
|
|
|
|
for (i = hunk_index - 1; i >= 0; i--)
|
2019-12-13 11:07:54 +03:00
|
|
|
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
undecided_previous = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
undecided_next = -1;
|
2019-12-13 11:07:54 +03:00
|
|
|
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
|
|
|
|
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
undecided_next = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Everything decided? */
|
|
|
|
if (undecided_previous < 0 && undecided_next < 0 &&
|
|
|
|
hunk->use != UNDECIDED_HUNK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
strbuf_reset(&s->buf);
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
render_hunk(s, hunk, 0, colored, &s->buf);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
fputs(s->buf.buf, stdout);
|
|
|
|
|
|
|
|
strbuf_reset(&s->buf);
|
|
|
|
if (undecided_previous >= 0)
|
|
|
|
strbuf_addstr(&s->buf, ",k");
|
|
|
|
if (hunk_index)
|
|
|
|
strbuf_addstr(&s->buf, ",K");
|
|
|
|
if (undecided_next >= 0)
|
|
|
|
strbuf_addstr(&s->buf, ",j");
|
2019-12-13 11:07:54 +03:00
|
|
|
if (hunk_index + 1 < file_diff->hunk_nr)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
strbuf_addstr(&s->buf, ",J");
|
2019-12-13 11:08:02 +03:00
|
|
|
if (file_diff->hunk_nr > 1)
|
|
|
|
strbuf_addstr(&s->buf, ",g");
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
if (hunk->splittable_into > 1)
|
|
|
|
strbuf_addstr(&s->buf, ",s");
|
2019-12-13 11:08:01 +03:00
|
|
|
if (hunk_index + 1 > file_diff->mode_change &&
|
|
|
|
!file_diff->deleted)
|
|
|
|
strbuf_addstr(&s->buf, ",e");
|
2019-12-13 11:07:57 +03:00
|
|
|
|
|
|
|
if (file_diff->deleted)
|
|
|
|
prompt_mode_type = PROMPT_DELETION;
|
|
|
|
else if (file_diff->mode_change && !hunk_index)
|
|
|
|
prompt_mode_type = PROMPT_MODE_CHANGE;
|
|
|
|
else
|
|
|
|
prompt_mode_type = PROMPT_HUNK;
|
|
|
|
|
2019-12-13 11:07:52 +03:00
|
|
|
color_fprintf(stdout, s->s.prompt_color,
|
|
|
|
"(%"PRIuMAX"/%"PRIuMAX") ",
|
2019-12-13 11:07:54 +03:00
|
|
|
(uintmax_t)hunk_index + 1,
|
|
|
|
(uintmax_t)file_diff->hunk_nr);
|
2019-12-13 11:07:52 +03:00
|
|
|
color_fprintf(stdout, s->s.prompt_color,
|
2019-12-13 11:07:57 +03:00
|
|
|
_(prompt_mode[prompt_mode_type]), s->buf.buf);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
fflush(stdout);
|
|
|
|
if (strbuf_getline(&s->answer, stdin) == EOF)
|
|
|
|
break;
|
|
|
|
strbuf_trim_trailing_newline(&s->answer);
|
|
|
|
|
|
|
|
if (!s->answer.len)
|
|
|
|
continue;
|
|
|
|
ch = tolower(s->answer.buf[0]);
|
|
|
|
if (ch == 'y') {
|
|
|
|
hunk->use = USE_HUNK;
|
|
|
|
soft_increment:
|
|
|
|
hunk_index = undecided_next < 0 ?
|
2019-12-13 11:07:54 +03:00
|
|
|
file_diff->hunk_nr : undecided_next;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
} else if (ch == 'n') {
|
|
|
|
hunk->use = SKIP_HUNK;
|
|
|
|
goto soft_increment;
|
|
|
|
} else if (ch == 'a') {
|
2019-12-13 11:07:54 +03:00
|
|
|
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
|
|
|
|
hunk = file_diff->hunk + hunk_index;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
if (hunk->use == UNDECIDED_HUNK)
|
|
|
|
hunk->use = USE_HUNK;
|
|
|
|
}
|
|
|
|
} else if (ch == 'd') {
|
2019-12-13 11:07:54 +03:00
|
|
|
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
|
|
|
|
hunk = file_diff->hunk + hunk_index;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
if (hunk->use == UNDECIDED_HUNK)
|
|
|
|
hunk->use = SKIP_HUNK;
|
|
|
|
}
|
2019-12-13 11:07:53 +03:00
|
|
|
} else if (s->answer.buf[0] == 'K') {
|
|
|
|
if (hunk_index)
|
|
|
|
hunk_index--;
|
|
|
|
else
|
|
|
|
err(s, _("No previous hunk"));
|
|
|
|
} else if (s->answer.buf[0] == 'J') {
|
2019-12-13 11:07:54 +03:00
|
|
|
if (hunk_index + 1 < file_diff->hunk_nr)
|
2019-12-13 11:07:53 +03:00
|
|
|
hunk_index++;
|
|
|
|
else
|
|
|
|
err(s, _("No next hunk"));
|
|
|
|
} else if (s->answer.buf[0] == 'k') {
|
|
|
|
if (undecided_previous >= 0)
|
|
|
|
hunk_index = undecided_previous;
|
|
|
|
else
|
|
|
|
err(s, _("No previous hunk"));
|
|
|
|
} else if (s->answer.buf[0] == 'j') {
|
|
|
|
if (undecided_next >= 0)
|
|
|
|
hunk_index = undecided_next;
|
|
|
|
else
|
|
|
|
err(s, _("No next hunk"));
|
2019-12-13 11:08:02 +03:00
|
|
|
} else if (s->answer.buf[0] == 'g') {
|
|
|
|
char *pend;
|
|
|
|
unsigned long response;
|
|
|
|
|
|
|
|
if (file_diff->hunk_nr < 2) {
|
|
|
|
err(s, _("No other hunks to goto"));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
strbuf_remove(&s->answer, 0, 1);
|
|
|
|
strbuf_trim(&s->answer);
|
|
|
|
i = hunk_index - DISPLAY_HUNKS_LINES / 2;
|
|
|
|
if (i < file_diff->mode_change)
|
|
|
|
i = file_diff->mode_change;
|
|
|
|
while (s->answer.len == 0) {
|
|
|
|
i = display_hunks(s, file_diff, i);
|
|
|
|
printf("%s", i < file_diff->hunk_nr ?
|
|
|
|
_("go to which hunk (<ret> to see "
|
|
|
|
"more)? ") : _("go to which hunk? "));
|
|
|
|
fflush(stdout);
|
|
|
|
if (strbuf_getline(&s->answer,
|
|
|
|
stdin) == EOF)
|
|
|
|
break;
|
|
|
|
strbuf_trim_trailing_newline(&s->answer);
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_trim(&s->answer);
|
|
|
|
response = strtoul(s->answer.buf, &pend, 10);
|
|
|
|
if (*pend || pend == s->answer.buf)
|
|
|
|
err(s, _("Invalid number: '%s'"),
|
|
|
|
s->answer.buf);
|
|
|
|
else if (0 < response && response <= file_diff->hunk_nr)
|
|
|
|
hunk_index = response - 1;
|
|
|
|
else
|
|
|
|
err(s, Q_("Sorry, only %d hunk available.",
|
|
|
|
"Sorry, only %d hunks available.",
|
|
|
|
file_diff->hunk_nr),
|
|
|
|
(int)file_diff->hunk_nr);
|
built-in add -p: implement the hunk splitting feature
If this developer's workflow is any indication, then this is *the* most
useful feature of Git's interactive `add `command.
Note: once again, this is not a verbatim conversion from the Perl code
to C: the `hunk_splittable()` function, for example, essentially did all
the work of splitting the hunk, just to find out whether more than one
hunk would have been the result (and then tossed that result into the
trash). In C we instead count the number of resulting hunks (without
actually doing the work of splitting, but just counting the transitions
from non-context lines to context lines), and store that information
with the hunk, and we do that *while* parsing the diff in the first
place.
Another deviation: the built-in `git add -p` was designed with a single
strbuf holding the diff (and another one holding the colored diff, if
that one was asked for) in mind, and hunks essentially store just the
start and end offsets pointing into that strbuf. As a consequence, when
we split hunks, we now use a special mode where the hunk header is
generated dynamically, and only the rest of the hunk is stored using
such start/end offsets. This way, we also avoid the frequent
formatting/re-parsing of the hunk header of the Perl version.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:58 +03:00
|
|
|
} else if (s->answer.buf[0] == 's') {
|
|
|
|
size_t splittable_into = hunk->splittable_into;
|
|
|
|
if (splittable_into < 2)
|
|
|
|
err(s, _("Sorry, cannot split this hunk"));
|
|
|
|
else if (!split_hunk(s, file_diff,
|
|
|
|
hunk - file_diff->hunk))
|
|
|
|
color_fprintf_ln(stdout, s->s.header_color,
|
|
|
|
_("Split into %d hunks."),
|
|
|
|
(int)splittable_into);
|
2019-12-13 11:08:01 +03:00
|
|
|
} else if (s->answer.buf[0] == 'e') {
|
|
|
|
if (hunk_index + 1 == file_diff->mode_change)
|
|
|
|
err(s, _("Sorry, cannot edit this hunk"));
|
|
|
|
else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
|
|
|
|
hunk->use = USE_HUNK;
|
|
|
|
goto soft_increment;
|
|
|
|
}
|
2019-12-13 11:07:53 +03:00
|
|
|
} else
|
2019-12-13 11:07:52 +03:00
|
|
|
color_fprintf(stdout, s->s.help_color,
|
|
|
|
_(help_patch_text));
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Any hunk to be used? */
|
2019-12-13 11:07:54 +03:00
|
|
|
for (i = 0; i < file_diff->hunk_nr; i++)
|
|
|
|
if (file_diff->hunk[i].use == USE_HUNK)
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
break;
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
if (i < file_diff->hunk_nr) {
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
/* At least one hunk selected: apply */
|
|
|
|
strbuf_reset(&s->buf);
|
2019-12-13 11:08:01 +03:00
|
|
|
reassemble_patch(s, file_diff, 0, &s->buf);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
discard_index(s->s.r->index);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
setup_child_process(s, &cp, "apply", "--cached", NULL);
|
|
|
|
if (pipe_command(&cp, s->buf.buf, s->buf.len,
|
|
|
|
NULL, 0, NULL, 0))
|
|
|
|
error(_("'git apply --cached' failed"));
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
if (!repo_read_index(s->s.r))
|
|
|
|
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
1, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
putchar('\n');
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int run_add_p(struct repository *r, const struct pathspec *ps)
|
|
|
|
{
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
struct add_p_state s = {
|
|
|
|
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
|
|
|
|
};
|
2019-12-13 11:07:54 +03:00
|
|
|
size_t i;
|
built-in add -p: adjust hunk headers as needed
When skipping a hunk that adds a different number of lines than it
removes, we need to adjust the subsequent hunk headers of non-skipped
hunks: in pathological cases, the context is not enough to determine
precisely where the patch should be applied.
This problem was identified in 23fea4c240 (t3701: add failing test for
pathological context lines, 2018-03-01) and fixed in the Perl version in
fecc6f3a68 (add -p: adjust offsets of subsequent hunks when one is
skipped, 2018-03-01).
And this patch fixes it in the C version of `git add -p`.
In contrast to the Perl version, we try to keep the extra text on the
hunk header (which typically contains the signature of the function
whose code is changed in the hunk) intact.
Note: while the C version does not support staging mode changes at this
stage, we already prepare for this by simply skipping the hunk header if
both old and new offset is 0 (this cannot happen for regular hunks, and
we will use this as an indicator that we are looking at a special hunk).
Likewise, we already prepare for hunk splitting by handling the absence
of extra text in the hunk header gracefully: only the first split hunk
will have that text, the others will not (indicated by an empty extra
text start/end range). Preparing for hunk splitting already at this
stage avoids an indentation change of the entire hunk header-printing
block later, and is almost as easy to review as without that handling.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:51 +03:00
|
|
|
|
|
|
|
init_add_i_state(&s.s, r);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
|
|
|
if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
|
|
|
|
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
|
|
|
|
NULL, NULL, NULL) < 0 ||
|
|
|
|
parse_diff(&s, ps) < 0) {
|
|
|
|
strbuf_release(&s.plain);
|
2019-12-13 11:07:50 +03:00
|
|
|
strbuf_release(&s.colored);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:07:54 +03:00
|
|
|
for (i = 0; i < s.file_diff_nr; i++)
|
|
|
|
if (patch_update_file(&s, s.file_diff + i))
|
|
|
|
break;
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
|
|
|
|
strbuf_release(&s.answer);
|
|
|
|
strbuf_release(&s.buf);
|
|
|
|
strbuf_release(&s.plain);
|
2019-12-13 11:07:50 +03:00
|
|
|
strbuf_release(&s.colored);
|
built-in add -i: start implementing the `patch` functionality in C
In the previous steps, we re-implemented the main loop of `git add -i`
in C, and most of the commands.
Notably, we left out the actual functionality of `patch`, as the
relevant code makes up more than half of `git-add--interactive.perl`,
and is actually pretty independent of the rest of the commands.
With this commit, we start to tackle that `patch` part. For better
separation of concerns, we keep the code in a separate file,
`add-patch.c`. The new code is still guarded behind the
`add.interactive.useBuiltin` config setting, and for the moment,
it can only be called via `git add -p`.
The actual functionality follows the original implementation of
5cde71d64aff (git-add --interactive, 2006-12-10), but not too closely
(for example, we use string offsets rather than copying strings around,
and after seeing whether the `k` and `j` commands are applicable, in the
C version we remember which previous/next hunk was undecided, and use it
rather than looking again when the user asked to jump).
As a further deviation from that commit, We also use a comma instead of
a slash to separate the available commands in the prompt, as the current
version of the Perl script does this, and we also add a line about the
question mark ("print help") to the help text.
While it is tempting to use this conversion of `git add -p` as an excuse
to work on `apply_all_patches()` so that it does _not_ want to read a
file from `stdin` or from a file, but accepts, say, an `strbuf` instead,
we will refrain from this particular rabbit hole at this stage.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-12-13 11:07:48 +03:00
|
|
|
return 0;
|
|
|
|
}
|