2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* apply.c
|
|
|
|
*
|
|
|
|
* Copyright (C) Linus Torvalds, 2005
|
|
|
|
*
|
|
|
|
* This applies patches on top of some (arbitrary) version of the SCM.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
global: introduce `USE_THE_REPOSITORY_VARIABLE` macro
Use of the `the_repository` variable is deprecated nowadays, and we
slowly but steadily convert the codebase to not use it anymore. Instead,
callers should be passing down the repository to work on via parameters.
It is hard though to prove that a given code unit does not use this
variable anymore. The most trivial case, merely demonstrating that there
is no direct use of `the_repository`, is already a bit of a pain during
code reviews as the reviewer needs to manually verify claims made by the
patch author. The bigger problem though is that we have many interfaces
that implicitly rely on `the_repository`.
Introduce a new `USE_THE_REPOSITORY_VARIABLE` macro that allows code
units to opt into usage of `the_repository`. The intent of this macro is
to demonstrate that a certain code unit does not use this variable
anymore, and to keep it from new dependencies on it in future changes,
be it explicit or implicit
For now, the macro only guards `the_repository` itself as well as
`the_hash_algo`. There are many more known interfaces where we have an
implicit dependency on `the_repository`, but those are not guarded at
the current point in time. Over time though, we should start to add
guards as required (or even better, just remove them).
Define the macro as required in our code units. As expected, most of our
code still relies on the global variable. Nearly all of our builtins
rely on the variable as there is no way yet to pass `the_repository` to
their entry point. For now, declare the macro in "biultin.h" to keep the
required changes at least a little bit more contained.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-06-14 14:50:23 +08:00
|
|
|
#define USE_THE_REPOSITORY_VARIABLE
|
|
|
|
|
2023-05-16 14:33:57 +08:00
|
|
|
#include "git-compat-util.h"
|
2023-03-21 14:25:58 +08:00
|
|
|
#include "abspath.h"
|
2023-04-23 04:17:13 +08:00
|
|
|
#include "base85.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2023-05-16 14:34:06 +08:00
|
|
|
#include "object-store-ll.h"
|
2016-04-23 02:55:46 +08:00
|
|
|
#include "delta.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "dir.h"
|
2023-03-21 14:26:03 +08:00
|
|
|
#include "environment.h"
|
2023-03-21 14:25:54 +08:00
|
|
|
#include "gettext.h"
|
2023-02-24 08:09:27 +08:00
|
|
|
#include "hex.h"
|
2016-04-23 02:55:46 +08:00
|
|
|
#include "xdiff-interface.h"
|
merge-ll: rename from ll-merge
A long term (but rather minor) pet-peeve of mine was the name
ll-merge.[ch]. I thought it made it harder to realize what stuff was
related to merging when I was working on the merge machinery and trying
to improve it.
Further, back in d1cbe1e6d8a ("hash-ll.h: split out of hash.h to remove
dependency on repository.h", 2023-04-22), we have split the portions of
hash.h that do not depend upon repository.h into a "hash-ll.h" (due to
the recommendation to use "ll" for "low-level" in its name[1], but which
I used as a suffix precisely because of my distaste for "ll-merge").
When we discussed adding additional "*-ll.h" files, a request was made
that we use "ll" consistently as either a prefix or a suffix. Since it
is already in use as both a prefix and a suffix, the only way to do so
is to rename some files.
Besides my distaste for the ll-merge.[ch] name, let me also note that
the files
ll-fsmonitor.h, ll-hash.h, ll-merge.h, ll-object-store.h, ll-read-cache.h
would have essentially nothing to do with each other and make no sense
to group. But giving them the common "ll-" prefix would group them. Using
"-ll" as a suffix thus seems just much more logical to me. Rename
ll-merge.[ch] to merge-ll.[ch] to achieve this consistency, and to
ensure we get a more logical grouping of files.
[1] https://lore.kernel.org/git/kl6lsfcu1g8w.fsf@chooglen-macbookpro.roam.corp.google.com/
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-05-16 14:34:04 +08:00
|
|
|
#include "merge-ll.h"
|
2016-08-09 05:03:07 +08:00
|
|
|
#include "lockfile.h"
|
2023-05-16 14:33:50 +08:00
|
|
|
#include "name-hash.h"
|
2023-04-11 15:41:49 +08:00
|
|
|
#include "object-name.h"
|
2023-04-11 15:41:53 +08:00
|
|
|
#include "object-file.h"
|
2016-04-23 02:55:46 +08:00
|
|
|
#include "parse-options.h"
|
2023-05-16 14:33:59 +08:00
|
|
|
#include "path.h"
|
2016-04-23 02:55:46 +08:00
|
|
|
#include "quote.h"
|
2023-05-16 14:33:56 +08:00
|
|
|
#include "read-cache.h"
|
2024-09-12 19:29:24 +08:00
|
|
|
#include "repository.h"
|
2016-04-23 02:55:46 +08:00
|
|
|
#include "rerere.h"
|
2016-08-09 05:03:07 +08:00
|
|
|
#include "apply.h"
|
2021-03-23 22:19:32 +08:00
|
|
|
#include "entry.h"
|
2023-03-21 14:26:05 +08:00
|
|
|
#include "setup.h"
|
2023-04-23 04:17:09 +08:00
|
|
|
#include "symlinks.h"
|
2023-05-16 14:34:03 +08:00
|
|
|
#include "wildmatch.h"
|
2023-04-23 04:17:16 +08:00
|
|
|
#include "ws.h"
|
2016-08-09 05:03:07 +08:00
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
struct gitdiff_data {
|
|
|
|
struct strbuf *root;
|
|
|
|
int linenr;
|
|
|
|
int p_value;
|
|
|
|
};
|
|
|
|
|
2016-08-09 05:03:07 +08:00
|
|
|
static void git_apply_config(void)
|
|
|
|
{
|
config: drop git_config_get_string_const()
As evidenced by the leak fixes in the previous commit, the "const" in
git_config_get_string_const() clearly misleads people into thinking that
it does not allocate a copy of the string. We can fix this by renaming
it, but it's easier still to just drop it. Of the four remaining
callers:
- The one in git_config_parse_expiry() still needs to allocate, since
that's what its callers expect. We can just use the non-const
version and cast our pointer. Slightly ugly, but the damage is
contained in one spot.
- The two in apply are writing to global "const char *" variables, and
need to continue allocating. We often mark these as const because we
assign default string literals to them. But in this case we don't do
that, so we can just declare them as real "char *" pointers and use
the non-const version.
- The call in checkout doesn't actually need a copy; it can just use
the non-allocating "tmp" version of the function.
The function is also mentioned in the MyFirstContribution document. We
can swap that call out for the non-allocating "tmp" variant, which fits
well in the example given.
We'll drop the "configset" and "repo" variants, as well (which are
unused).
Note that this frees up the "const" name, so we could rename the "tmp"
variant back to that. But let's give some time for topics in flight to
adapt to the new code before doing so (if we do it too soon, the
function semantics will change but the compiler won't alert us).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-08-18 05:33:11 +08:00
|
|
|
git_config_get_string("apply.whitespace", &apply_default_whitespace);
|
|
|
|
git_config_get_string("apply.ignorewhitespace", &apply_default_ignorewhitespace);
|
2019-10-24 07:32:38 +08:00
|
|
|
git_config(git_xmerge_config, NULL);
|
2016-08-09 05:03:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:23 +08:00
|
|
|
static int parse_whitespace_option(struct apply_state *state, const char *option)
|
2016-08-09 05:03:07 +08:00
|
|
|
{
|
|
|
|
if (!option) {
|
|
|
|
state->ws_error_action = warn_on_ws_error;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(option, "warn")) {
|
|
|
|
state->ws_error_action = warn_on_ws_error;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(option, "nowarn")) {
|
|
|
|
state->ws_error_action = nowarn_ws_error;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(option, "error")) {
|
|
|
|
state->ws_error_action = die_on_ws_error;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(option, "error-all")) {
|
|
|
|
state->ws_error_action = die_on_ws_error;
|
|
|
|
state->squelch_whitespace_errors = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
|
|
|
|
state->ws_error_action = correct_ws_error;
|
|
|
|
return 0;
|
|
|
|
}
|
2019-02-16 19:24:41 +08:00
|
|
|
/*
|
2024-02-15 02:44:01 +08:00
|
|
|
* Please update $__git_whitespacelist in git-completion.bash,
|
|
|
|
* Documentation/git-apply.txt, and Documentation/git-am.txt
|
2019-02-16 19:24:41 +08:00
|
|
|
* when you add new options.
|
|
|
|
*/
|
2016-08-09 05:03:07 +08:00
|
|
|
return error(_("unrecognized whitespace option '%s'"), option);
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:23 +08:00
|
|
|
static int parse_ignorewhitespace_option(struct apply_state *state,
|
|
|
|
const char *option)
|
2016-08-09 05:03:07 +08:00
|
|
|
{
|
|
|
|
if (!option || !strcmp(option, "no") ||
|
|
|
|
!strcmp(option, "false") || !strcmp(option, "never") ||
|
|
|
|
!strcmp(option, "none")) {
|
|
|
|
state->ws_ignore_action = ignore_ws_none;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(option, "change")) {
|
|
|
|
state->ws_ignore_action = ignore_ws_change;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return error(_("unrecognized whitespace ignore option '%s'"), option);
|
|
|
|
}
|
|
|
|
|
2016-08-09 05:03:08 +08:00
|
|
|
int init_apply_state(struct apply_state *state,
|
2018-08-14 00:14:39 +08:00
|
|
|
struct repository *repo,
|
apply: move lockfile into `apply_state`
We have two users of `struct apply_state` and the related functionality
in apply.c. Each user sets up its `apply_state` by handing over a
pointer to its static `lock_file`. (Before 076aa2cbd (tempfile:
auto-allocate tempfiles on heap, 2017-09-05), we could never free
lockfiles, so making them static was a reasonable approach.)
Other than that, they never directly access their `lock_file`s, which
are instead handled by the functionality in apply.c.
To make life easier for the caller and to make it less tempting for a
future caller to mess with the lock, make apply.c fully responsible for
setting up the `lock_file`. As mentioned above, it is now safe to free a
`lock_file`, so we can make the `struct apply_state` contain an actual
`struct lock_file` instead of a pointer to one.
The user in builtin/apply.c is rather simple. For builtin/am.c, we might
worry that the lock state is actually meant to be inherited across
calls. But the lock is only taken as `apply_all_patches()` executes, and
code inspection shows that it will always be released.
Alternatively, we can observe that the lock itself is never queried
directly. When we decide whether we should lock, we check a related
variable `newfd`. That variable is not inherited, so from the point of
view of apply.c, the state machine really is reset with each call to
`init_apply_state()`. (It would be a bug if `newfd` and the lock status
were not in sync. The duplication of information in `newfd` and the lock
will be addressed in the next patch.)
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-06 04:32:09 +08:00
|
|
|
const char *prefix)
|
2016-08-09 05:03:07 +08:00
|
|
|
{
|
|
|
|
memset(state, 0, sizeof(*state));
|
|
|
|
state->prefix = prefix;
|
2018-08-14 00:14:39 +08:00
|
|
|
state->repo = repo;
|
2016-08-09 05:03:07 +08:00
|
|
|
state->apply = 1;
|
|
|
|
state->line_termination = '\n';
|
|
|
|
state->p_value = 1;
|
|
|
|
state->p_context = UINT_MAX;
|
|
|
|
state->squelch_whitespace_errors = 5;
|
|
|
|
state->ws_error_action = warn_on_ws_error;
|
|
|
|
state->ws_ignore_action = ignore_ws_none;
|
|
|
|
state->linenr = 1;
|
2021-07-01 18:51:29 +08:00
|
|
|
string_list_init_nodup(&state->fn_table);
|
|
|
|
string_list_init_nodup(&state->limit_by_name);
|
2022-01-07 20:16:53 +08:00
|
|
|
strset_init(&state->removed_symlinks);
|
|
|
|
strset_init(&state->kept_symlinks);
|
2016-08-09 05:03:07 +08:00
|
|
|
strbuf_init(&state->root, 0);
|
|
|
|
|
|
|
|
git_apply_config();
|
|
|
|
if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace))
|
2016-08-09 05:03:08 +08:00
|
|
|
return -1;
|
2016-08-09 05:03:07 +08:00
|
|
|
if (apply_default_ignorewhitespace && parse_ignorewhitespace_option(state, apply_default_ignorewhitespace))
|
2016-08-09 05:03:08 +08:00
|
|
|
return -1;
|
|
|
|
return 0;
|
2016-08-09 05:03:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void clear_apply_state(struct apply_state *state)
|
|
|
|
{
|
|
|
|
string_list_clear(&state->limit_by_name, 0);
|
2022-01-07 20:16:53 +08:00
|
|
|
strset_clear(&state->removed_symlinks);
|
|
|
|
strset_clear(&state->kept_symlinks);
|
2016-08-09 05:03:07 +08:00
|
|
|
strbuf_release(&state->root);
|
2024-06-11 17:19:22 +08:00
|
|
|
FREE_AND_NULL(state->fake_ancestor);
|
2016-08-09 05:03:07 +08:00
|
|
|
|
|
|
|
/* &state->fn_table is cleared at the end of apply_patch() */
|
|
|
|
}
|
2016-08-09 05:03:10 +08:00
|
|
|
|
2022-10-18 09:08:51 +08:00
|
|
|
static void mute_routine(const char *msg UNUSED, va_list params UNUSED)
|
2016-09-05 04:18:29 +08:00
|
|
|
{
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
|
2016-08-09 05:03:10 +08:00
|
|
|
int check_apply_state(struct apply_state *state, int force_apply)
|
|
|
|
{
|
|
|
|
int is_not_gitdir = !startup_info->have_repository;
|
|
|
|
|
|
|
|
if (state->apply_with_reject && state->threeway)
|
2022-01-06 04:02:16 +08:00
|
|
|
return error(_("options '%s' and '%s' cannot be used together"), "--reject", "--3way");
|
2016-08-09 05:03:10 +08:00
|
|
|
if (state->threeway) {
|
|
|
|
if (is_not_gitdir)
|
2022-01-06 04:02:22 +08:00
|
|
|
return error(_("'%s' outside a repository"), "--3way");
|
2016-08-09 05:03:10 +08:00
|
|
|
state->check_index = 1;
|
|
|
|
}
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_with_reject) {
|
|
|
|
state->apply = 1;
|
|
|
|
if (state->apply_verbosity == verbosity_normal)
|
|
|
|
state->apply_verbosity = verbosity_verbose;
|
|
|
|
}
|
2016-08-09 05:03:10 +08:00
|
|
|
if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
|
|
|
|
state->apply = 0;
|
|
|
|
if (state->check_index && is_not_gitdir)
|
2022-01-06 04:02:22 +08:00
|
|
|
return error(_("'%s' outside a repository"), "--index");
|
2016-08-09 05:03:10 +08:00
|
|
|
if (state->cached) {
|
|
|
|
if (is_not_gitdir)
|
2022-01-06 04:02:22 +08:00
|
|
|
return error(_("'%s' outside a repository"), "--cached");
|
2016-08-09 05:03:10 +08:00
|
|
|
state->check_index = 1;
|
|
|
|
}
|
2018-05-26 20:08:46 +08:00
|
|
|
if (state->ita_only && (state->check_index || is_not_gitdir))
|
|
|
|
state->ita_only = 0;
|
2016-08-09 05:03:10 +08:00
|
|
|
if (state->check_index)
|
|
|
|
state->unsafe_paths = 0;
|
|
|
|
|
2016-09-05 04:18:29 +08:00
|
|
|
if (state->apply_verbosity <= verbosity_silent) {
|
|
|
|
state->saved_error_routine = get_error_routine();
|
|
|
|
state->saved_warn_routine = get_warn_routine();
|
|
|
|
set_error_routine(mute_routine);
|
|
|
|
set_warn_routine(mute_routine);
|
|
|
|
}
|
|
|
|
|
2016-08-09 05:03:10 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
static void set_default_whitespace_mode(struct apply_state *state)
|
|
|
|
{
|
|
|
|
if (!state->whitespace_option && !apply_default_whitespace)
|
|
|
|
state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This represents one "hunk" from a patch, starting with
|
|
|
|
* "@@ -oldpos,oldlines +newpos,newlines @@" marker. The
|
|
|
|
* patch text is pointed at by patch, and its byte length
|
|
|
|
* is stored in size. leading and trailing are the number
|
|
|
|
* of context lines.
|
|
|
|
*/
|
|
|
|
struct fragment {
|
|
|
|
unsigned long leading, trailing;
|
|
|
|
unsigned long oldpos, oldlines;
|
|
|
|
unsigned long newpos, newlines;
|
|
|
|
/*
|
|
|
|
* 'patch' is usually borrowed from buf in apply_patch(),
|
|
|
|
* but some codepaths store an allocated buffer.
|
|
|
|
*/
|
|
|
|
const char *patch;
|
|
|
|
unsigned free_patch:1,
|
|
|
|
rejected:1;
|
|
|
|
int size;
|
|
|
|
int linenr;
|
|
|
|
struct fragment *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When dealing with a binary patch, we reuse "leading" field
|
|
|
|
* to store the type of the binary hunk, either deflated "delta"
|
|
|
|
* or deflated "literal".
|
|
|
|
*/
|
|
|
|
#define binary_patch_method leading
|
|
|
|
#define BINARY_DELTA_DEFLATED 1
|
|
|
|
#define BINARY_LITERAL_DEFLATED 2
|
|
|
|
|
|
|
|
static void free_fragment_list(struct fragment *list)
|
|
|
|
{
|
|
|
|
while (list) {
|
|
|
|
struct fragment *next = list->next;
|
|
|
|
if (list->free_patch)
|
|
|
|
free((char *)list->patch);
|
|
|
|
free(list);
|
|
|
|
list = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-05 02:32:15 +08:00
|
|
|
void release_patch(struct patch *patch)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
free_fragment_list(patch->fragments);
|
|
|
|
free(patch->def_name);
|
|
|
|
free(patch->old_name);
|
|
|
|
free(patch->new_name);
|
|
|
|
free(patch->result);
|
2022-03-05 02:32:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void free_patch(struct patch *patch)
|
|
|
|
{
|
|
|
|
release_patch(patch);
|
2016-04-23 02:55:46 +08:00
|
|
|
free(patch);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_patch_list(struct patch *list)
|
|
|
|
{
|
|
|
|
while (list) {
|
|
|
|
struct patch *next = list->next;
|
|
|
|
free_patch(list);
|
|
|
|
list = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A line in a file, len-bytes long (includes the terminating LF,
|
|
|
|
* except for an incomplete line at the end if the file ends with
|
|
|
|
* one), and its contents hashes to 'hash'.
|
|
|
|
*/
|
|
|
|
struct line {
|
|
|
|
size_t len;
|
|
|
|
unsigned hash : 24;
|
|
|
|
unsigned flag : 8;
|
|
|
|
#define LINE_COMMON 1
|
|
|
|
#define LINE_PATCHED 2
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This represents a "file", which is an array of "lines".
|
|
|
|
*/
|
|
|
|
struct image {
|
2024-09-17 18:08:08 +08:00
|
|
|
struct strbuf buf;
|
2016-04-23 02:55:46 +08:00
|
|
|
struct line *line;
|
2024-09-17 18:08:06 +08:00
|
|
|
size_t line_nr, line_alloc;
|
2016-04-23 02:55:46 +08:00
|
|
|
};
|
2024-09-17 18:08:08 +08:00
|
|
|
#define IMAGE_INIT { \
|
|
|
|
.buf = STRBUF_INIT, \
|
|
|
|
}
|
2024-09-17 18:08:01 +08:00
|
|
|
|
|
|
|
static void image_init(struct image *image)
|
|
|
|
{
|
|
|
|
struct image empty = IMAGE_INIT;
|
|
|
|
memcpy(image, &empty, sizeof(*image));
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-09-17 18:07:55 +08:00
|
|
|
static void image_clear(struct image *image)
|
2024-09-17 18:07:52 +08:00
|
|
|
{
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_release(&image->buf);
|
2024-09-17 18:08:03 +08:00
|
|
|
free(image->line);
|
2024-09-17 18:08:01 +08:00
|
|
|
image_init(image);
|
2024-09-17 18:07:52 +08:00
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
static uint32_t hash_line(const char *cp, size_t len)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
uint32_t h;
|
|
|
|
for (i = 0, h = 0; i < len; i++) {
|
|
|
|
if (!isspace(cp[i])) {
|
|
|
|
h = h * 3 + (cp[i] & 0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
2024-09-17 18:07:55 +08:00
|
|
|
static void image_add_line(struct image *img, const char *bol, size_t len, unsigned flag)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
2024-09-17 18:08:06 +08:00
|
|
|
ALLOC_GROW(img->line, img->line_nr + 1, img->line_alloc);
|
|
|
|
img->line[img->line_nr].len = len;
|
|
|
|
img->line[img->line_nr].hash = hash_line(bol, len);
|
|
|
|
img->line[img->line_nr].flag = flag;
|
|
|
|
img->line_nr++;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "buf" has the file contents to be patched (read from various sources).
|
|
|
|
* attach it to "image" and add line-based index to it.
|
|
|
|
* "image" now owns the "buf".
|
|
|
|
*/
|
2024-09-17 18:07:55 +08:00
|
|
|
static void image_prepare(struct image *image, char *buf, size_t len,
|
2016-04-23 02:55:46 +08:00
|
|
|
int prepare_linetable)
|
|
|
|
{
|
|
|
|
const char *cp, *ep;
|
|
|
|
|
2024-09-17 18:08:01 +08:00
|
|
|
image_clear(image);
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_attach(&image->buf, buf, len, len + 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
if (!prepare_linetable)
|
|
|
|
return;
|
|
|
|
|
2024-09-17 18:08:08 +08:00
|
|
|
ep = image->buf.buf + image->buf.len;
|
|
|
|
cp = image->buf.buf;
|
2016-04-23 02:55:46 +08:00
|
|
|
while (cp < ep) {
|
|
|
|
const char *next;
|
|
|
|
for (next = cp; next < ep && *next != '\n'; next++)
|
|
|
|
;
|
|
|
|
if (next < ep)
|
|
|
|
next++;
|
2024-09-17 18:07:55 +08:00
|
|
|
image_add_line(image, cp, next - cp, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
cp = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-17 18:07:55 +08:00
|
|
|
static void image_remove_first_line(struct image *img)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_remove(&img->buf, 0, img->line[0].len);
|
2024-09-17 18:08:06 +08:00
|
|
|
img->line_nr--;
|
|
|
|
if (img->line_nr)
|
|
|
|
MOVE_ARRAY(img->line, img->line + 1, img->line_nr);
|
2024-09-17 18:07:52 +08:00
|
|
|
}
|
|
|
|
|
2024-09-17 18:07:55 +08:00
|
|
|
static void image_remove_last_line(struct image *img)
|
2024-09-17 18:07:52 +08:00
|
|
|
{
|
2024-09-17 18:08:08 +08:00
|
|
|
size_t last_line_len = img->line[img->line_nr - 1].len;
|
|
|
|
strbuf_setlen(&img->buf, img->buf.len - last_line_len);
|
|
|
|
img->line_nr--;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* fmt must contain _one_ %s and no other substitution */
|
|
|
|
static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
|
|
|
|
{
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (patch->old_name && patch->new_name &&
|
|
|
|
strcmp(patch->old_name, patch->new_name)) {
|
|
|
|
quote_c_style(patch->old_name, &sb, NULL, 0);
|
|
|
|
strbuf_addstr(&sb, " => ");
|
|
|
|
quote_c_style(patch->new_name, &sb, NULL, 0);
|
|
|
|
} else {
|
|
|
|
const char *n = patch->new_name;
|
|
|
|
if (!n)
|
|
|
|
n = patch->old_name;
|
|
|
|
quote_c_style(n, &sb, NULL, 0);
|
|
|
|
}
|
|
|
|
fprintf(output, fmt, sb.buf);
|
|
|
|
fputc('\n', output);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SLOP (16)
|
|
|
|
|
apply: reject patches larger than ~1 GiB
The apply code is not prepared to handle extremely large files. It uses
"int" in some places, and "unsigned long" in others.
This combination leads to unfortunate problems when switching between
the two types. Using "int" prevents us from handling large files, since
large offsets will wrap around and spill into small negative values,
which can result in wrong behavior (like accessing the patch buffer with
a negative offset).
Converting from "unsigned long" to "int" also has truncation problems
even on LLP64 platforms where "long" is the same size as "int", since
the former is unsigned but the latter is not.
To avoid potential overflow and truncation issues in `git apply`, apply
similar treatment as in dcd1742e56 (xdiff: reject files larger than
~1GB, 2015-09-24), where the xdiff code was taught to reject large
files for similar reasons.
The maximum size was chosen somewhat arbitrarily, but picking a value
just shy of a gigabyte allows us to double it without overflowing 2^31-1
(after which point our value would wrap around to a negative number).
To give ourselves a bit of extra margin, the maximum patch size is a MiB
smaller than a full GiB, which gives us some slop in case we allocate
"(records + 1) * sizeof(int)" or similar.
Luckily, the security implications of these conversion issues are
relatively uninteresting, because a victim needs to be convinced to
apply a malicious patch.
Reported-by: 정재우 <thebound7@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-26 02:24:31 +08:00
|
|
|
/*
|
|
|
|
* apply.c isn't equipped to handle arbitrarily large patches, because
|
|
|
|
* it intermingles `unsigned long` with `int` for the type used to store
|
|
|
|
* buffer lengths.
|
|
|
|
*
|
|
|
|
* Only process patches that are just shy of 1 GiB large in order to
|
|
|
|
* avoid any truncation or overflow issues.
|
|
|
|
*/
|
|
|
|
#define MAX_APPLY_SIZE (1024UL * 1024 * 1023)
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
static int read_patch_file(struct strbuf *sb, int fd)
|
|
|
|
{
|
2023-06-26 17:37:33 +08:00
|
|
|
if (strbuf_read(sb, fd, 0) < 0)
|
|
|
|
return error_errno(_("failed to read patch"));
|
|
|
|
else if (sb->len >= MAX_APPLY_SIZE)
|
|
|
|
return error(_("patch too large"));
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* Make sure that we have some slop in the buffer
|
|
|
|
* so that we can do speculative "memcmp" etc, and
|
|
|
|
* see to it that it is NUL-filled.
|
|
|
|
*/
|
|
|
|
strbuf_grow(sb, SLOP);
|
|
|
|
memset(sb->buf + sb->len, 0, SLOP);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long linelen(const char *buffer, unsigned long size)
|
|
|
|
{
|
|
|
|
unsigned long len = 0;
|
|
|
|
while (size--) {
|
|
|
|
len++;
|
|
|
|
if (*buffer++ == '\n')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_dev_null(const char *str)
|
|
|
|
{
|
|
|
|
return skip_prefix(str, "/dev/null", &str) && isspace(*str);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TERM_SPACE 1
|
|
|
|
#define TERM_TAB 2
|
|
|
|
|
|
|
|
static int name_terminate(int c, int terminate)
|
|
|
|
{
|
|
|
|
if (c == ' ' && !(terminate & TERM_SPACE))
|
|
|
|
return 0;
|
|
|
|
if (c == '\t' && !(terminate & TERM_TAB))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove double slashes to make --index work with such filenames */
|
|
|
|
static char *squash_slash(char *name)
|
|
|
|
{
|
|
|
|
int i = 0, j = 0;
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
while (name[i]) {
|
|
|
|
if ((name[j++] = name[i++]) == '/')
|
|
|
|
while (name[i] == '/')
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
name[j] = '\0';
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
static char *find_name_gnu(struct strbuf *root,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
int p_value)
|
|
|
|
{
|
|
|
|
struct strbuf name = STRBUF_INIT;
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Proposed "new-style" GNU patch/diff format; see
|
2019-11-27 20:54:04 +08:00
|
|
|
* https://lore.kernel.org/git/7vll0wvb2a.fsf@assigned-by-dhcp.cox.net/
|
2016-04-23 02:55:46 +08:00
|
|
|
*/
|
|
|
|
if (unquote_c_style(&name, line, NULL)) {
|
|
|
|
strbuf_release(&name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cp = name.buf; p_value; p_value--) {
|
|
|
|
cp = strchr(cp, '/');
|
|
|
|
if (!cp) {
|
|
|
|
strbuf_release(&name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_remove(&name, 0, cp - name.buf);
|
2019-07-09 00:33:06 +08:00
|
|
|
if (root->len)
|
|
|
|
strbuf_insert(&name, 0, root->buf, root->len);
|
2016-04-23 02:55:46 +08:00
|
|
|
return squash_slash(strbuf_detach(&name, NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t sane_tz_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *tz, *p;
|
|
|
|
|
|
|
|
if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ')
|
|
|
|
return 0;
|
|
|
|
tz = line + len - strlen(" +0500");
|
|
|
|
|
|
|
|
if (tz[1] != '+' && tz[1] != '-')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (p = tz + 2; p != line + len; p++)
|
|
|
|
if (!isdigit(*p))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return line + len - tz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t tz_with_colon_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *tz, *p;
|
|
|
|
|
|
|
|
if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':')
|
|
|
|
return 0;
|
|
|
|
tz = line + len - strlen(" +08:00");
|
|
|
|
|
|
|
|
if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-'))
|
|
|
|
return 0;
|
|
|
|
p = tz + 2;
|
|
|
|
if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
|
|
|
|
!isdigit(*p++) || !isdigit(*p++))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return line + len - tz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t date_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *date, *p;
|
|
|
|
|
|
|
|
if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-')
|
|
|
|
return 0;
|
|
|
|
p = date = line + len - strlen("72-02-05");
|
|
|
|
|
|
|
|
if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
|
|
|
|
!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' ||
|
|
|
|
!isdigit(*p++) || !isdigit(*p++)) /* Not a date. */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (date - line >= strlen("19") &&
|
|
|
|
isdigit(date[-1]) && isdigit(date[-2])) /* 4-digit year */
|
|
|
|
date -= strlen("19");
|
|
|
|
|
|
|
|
return line + len - date;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t short_time_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *time, *p;
|
|
|
|
|
|
|
|
if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':')
|
|
|
|
return 0;
|
|
|
|
p = time = line + len - strlen(" 07:01:32");
|
|
|
|
|
|
|
|
/* Permit 1-digit hours? */
|
|
|
|
if (*p++ != ' ' ||
|
|
|
|
!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
|
|
|
|
!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' ||
|
|
|
|
!isdigit(*p++) || !isdigit(*p++)) /* Not a time. */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return line + len - time;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t fractional_time_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
/* Expected format: 19:41:17.620000023 */
|
|
|
|
if (!len || !isdigit(line[len - 1]))
|
|
|
|
return 0;
|
|
|
|
p = line + len - 1;
|
|
|
|
|
|
|
|
/* Fractional seconds. */
|
|
|
|
while (p > line && isdigit(*p))
|
|
|
|
p--;
|
|
|
|
if (*p != '.')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Hours, minutes, and whole seconds. */
|
|
|
|
n = short_time_len(line, p - line);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return line + len - p + n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t trailing_spaces_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
/* Expected format: ' ' x (1 or more) */
|
|
|
|
if (!len || line[len - 1] != ' ')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
p = line + len;
|
|
|
|
while (p != line) {
|
|
|
|
p--;
|
|
|
|
if (*p != ' ')
|
|
|
|
return line + len - (p + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* All spaces! */
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t diff_timestamp_len(const char *line, size_t len)
|
|
|
|
{
|
|
|
|
const char *end = line + len;
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Posix: 2010-07-05 19:41:17
|
|
|
|
* GNU: 2010-07-05 19:41:17.620000023 -0500
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!isdigit(end[-1]))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = sane_tz_len(line, end - line);
|
|
|
|
if (!n)
|
|
|
|
n = tz_with_colon_len(line, end - line);
|
|
|
|
end -= n;
|
|
|
|
|
|
|
|
n = short_time_len(line, end - line);
|
|
|
|
if (!n)
|
|
|
|
n = fractional_time_len(line, end - line);
|
|
|
|
end -= n;
|
|
|
|
|
|
|
|
n = date_len(line, end - line);
|
|
|
|
if (!n) /* No date. Too bad. */
|
|
|
|
return 0;
|
|
|
|
end -= n;
|
|
|
|
|
|
|
|
if (end == line) /* No space before date. */
|
|
|
|
return 0;
|
|
|
|
if (end[-1] == '\t') { /* Success! */
|
|
|
|
end--;
|
|
|
|
return line + len - end;
|
|
|
|
}
|
|
|
|
if (end[-1] != ' ') /* No space before date. */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Whitespace damage. */
|
|
|
|
end -= trailing_spaces_len(line, end - line);
|
|
|
|
return line + len - end;
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
static char *find_name_common(struct strbuf *root,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
const char *def,
|
|
|
|
int p_value,
|
|
|
|
const char *end,
|
|
|
|
int terminate)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
const char *start = NULL;
|
|
|
|
|
|
|
|
if (p_value == 0)
|
|
|
|
start = line;
|
|
|
|
while (line != end) {
|
|
|
|
char c = *line;
|
|
|
|
|
|
|
|
if (!end && isspace(c)) {
|
|
|
|
if (c == '\n')
|
|
|
|
break;
|
|
|
|
if (name_terminate(c, terminate))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
line++;
|
|
|
|
if (c == '/' && !--p_value)
|
|
|
|
start = line;
|
|
|
|
}
|
|
|
|
if (!start)
|
|
|
|
return squash_slash(xstrdup_or_null(def));
|
|
|
|
len = line - start;
|
|
|
|
if (!len)
|
|
|
|
return squash_slash(xstrdup_or_null(def));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generally we prefer the shorter name, especially
|
|
|
|
* if the other one is just a variation of that with
|
|
|
|
* something else tacked on to the end (ie "file.orig"
|
|
|
|
* or "file~").
|
|
|
|
*/
|
|
|
|
if (def) {
|
|
|
|
int deflen = strlen(def);
|
|
|
|
if (deflen < len && !strncmp(start, def, deflen))
|
|
|
|
return squash_slash(xstrdup(def));
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
if (root->len) {
|
|
|
|
char *ret = xstrfmt("%s%.*s", root->buf, len, start);
|
2016-04-23 02:55:46 +08:00
|
|
|
return squash_slash(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return squash_slash(xmemdupz(start, len));
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
static char *find_name(struct strbuf *root,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
char *def,
|
|
|
|
int p_value,
|
|
|
|
int terminate)
|
|
|
|
{
|
|
|
|
if (*line == '"') {
|
2019-07-09 00:33:06 +08:00
|
|
|
char *name = find_name_gnu(root, line, p_value);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (name)
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
return find_name_common(root, line, def, p_value, NULL, terminate);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
static char *find_name_traditional(struct strbuf *root,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
char *def,
|
|
|
|
int p_value)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
size_t date_len;
|
|
|
|
|
|
|
|
if (*line == '"') {
|
2019-07-09 00:33:06 +08:00
|
|
|
char *name = find_name_gnu(root, line, p_value);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (name)
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = strchrnul(line, '\n') - line;
|
|
|
|
date_len = diff_timestamp_len(line, len);
|
|
|
|
if (!date_len)
|
2019-07-09 00:33:06 +08:00
|
|
|
return find_name_common(root, line, def, p_value, NULL, TERM_TAB);
|
2016-04-23 02:55:46 +08:00
|
|
|
len -= date_len;
|
|
|
|
|
2019-07-09 00:33:06 +08:00
|
|
|
return find_name_common(root, line, def, p_value, line + len, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given the string after "--- " or "+++ ", guess the appropriate
|
|
|
|
* p_value for the given patch.
|
|
|
|
*/
|
|
|
|
static int guess_p_value(struct apply_state *state, const char *nameline)
|
|
|
|
{
|
|
|
|
char *name, *cp;
|
|
|
|
int val = -1;
|
|
|
|
|
|
|
|
if (is_dev_null(nameline))
|
|
|
|
return -1;
|
2019-07-09 00:33:06 +08:00
|
|
|
name = find_name_traditional(&state->root, nameline, NULL, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!name)
|
|
|
|
return -1;
|
|
|
|
cp = strchr(name, '/');
|
|
|
|
if (!cp)
|
|
|
|
val = 0;
|
|
|
|
else if (state->prefix) {
|
|
|
|
/*
|
|
|
|
* Does it begin with "a/$our-prefix" and such? Then this is
|
|
|
|
* very likely to apply to our directory.
|
|
|
|
*/
|
2017-08-09 23:54:46 +08:00
|
|
|
if (starts_with(name, state->prefix))
|
2016-04-23 02:55:46 +08:00
|
|
|
val = count_slashes(state->prefix);
|
|
|
|
else {
|
|
|
|
cp++;
|
2017-08-09 23:54:46 +08:00
|
|
|
if (starts_with(cp, state->prefix))
|
2016-04-23 02:55:46 +08:00
|
|
|
val = count_slashes(state->prefix) + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(name);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Does the ---/+++ line have the POSIX timestamp after the last HT?
|
|
|
|
* GNU diff puts epoch there to signal a creation/deletion event. Is
|
|
|
|
* this such a timestamp?
|
|
|
|
*/
|
|
|
|
static int has_epoch_timestamp(const char *nameline)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We are only interested in epoch timestamp; any non-zero
|
|
|
|
* fraction cannot be one, hence "(\.0+)?" in the regexp below.
|
|
|
|
* For the same reason, the date must be either 1969-12-31 or
|
|
|
|
* 1970-01-01, and the seconds part must be "00".
|
|
|
|
*/
|
|
|
|
const char stamp_regexp[] =
|
2017-08-26 03:06:28 +08:00
|
|
|
"^[0-2][0-9]:([0-5][0-9]):00(\\.0+)?"
|
2016-04-23 02:55:46 +08:00
|
|
|
" "
|
|
|
|
"([-+][0-2][0-9]:?[0-5][0-9])\n";
|
|
|
|
const char *timestamp = NULL, *cp, *colon;
|
|
|
|
static regex_t *stamp;
|
|
|
|
regmatch_t m[10];
|
2017-08-26 03:04:54 +08:00
|
|
|
int zoneoffset, epoch_hour, hour, minute;
|
2016-04-23 02:55:46 +08:00
|
|
|
int status;
|
|
|
|
|
|
|
|
for (cp = nameline; *cp != '\n'; cp++) {
|
|
|
|
if (*cp == '\t')
|
|
|
|
timestamp = cp + 1;
|
|
|
|
}
|
|
|
|
if (!timestamp)
|
|
|
|
return 0;
|
2017-08-26 03:04:54 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
|
|
|
|
* (west of GMT) or 1970-01-01 (east of GMT)
|
|
|
|
*/
|
2017-08-26 03:06:28 +08:00
|
|
|
if (skip_prefix(timestamp, "1969-12-31 ", ×tamp))
|
2017-08-26 03:04:54 +08:00
|
|
|
epoch_hour = 24;
|
2017-08-26 03:06:28 +08:00
|
|
|
else if (skip_prefix(timestamp, "1970-01-01 ", ×tamp))
|
2017-08-26 03:04:54 +08:00
|
|
|
epoch_hour = 0;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!stamp) {
|
|
|
|
stamp = xmalloc(sizeof(*stamp));
|
|
|
|
if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
|
|
|
|
warning(_("Cannot prepare timestamp regexp %s"),
|
|
|
|
stamp_regexp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
|
|
|
|
if (status) {
|
|
|
|
if (status != REG_NOMATCH)
|
|
|
|
warning(_("regexec returned %d for input: %s"),
|
|
|
|
status, timestamp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-26 03:06:28 +08:00
|
|
|
hour = strtol(timestamp, NULL, 10);
|
|
|
|
minute = strtol(timestamp + m[1].rm_so, NULL, 10);
|
2017-08-26 03:04:54 +08:00
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
|
|
|
|
if (*colon == ':')
|
|
|
|
zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
|
|
|
|
else
|
|
|
|
zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
|
|
|
|
if (timestamp[m[3].rm_so] == '-')
|
|
|
|
zoneoffset = -zoneoffset;
|
|
|
|
|
2017-08-26 03:04:54 +08:00
|
|
|
return hour * 60 + minute - zoneoffset == epoch_hour * 60;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the name etc info from the ---/+++ lines of a traditional patch header
|
|
|
|
*
|
|
|
|
* FIXME! The end-of-filename heuristics are kind of screwy. For existing
|
|
|
|
* files, we can happily check the index for a match, but for creating a
|
|
|
|
* new file we should try to match whatever "patch" does. I have no idea.
|
|
|
|
*/
|
|
|
|
static int parse_traditional_patch(struct apply_state *state,
|
|
|
|
const char *first,
|
|
|
|
const char *second,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
first += 4; /* skip "--- " */
|
|
|
|
second += 4; /* skip "+++ " */
|
|
|
|
if (!state->p_value_known) {
|
|
|
|
int p, q;
|
|
|
|
p = guess_p_value(state, first);
|
|
|
|
q = guess_p_value(state, second);
|
|
|
|
if (p < 0) p = q;
|
|
|
|
if (0 <= p && p == q) {
|
|
|
|
state->p_value = p;
|
|
|
|
state->p_value_known = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_dev_null(first)) {
|
|
|
|
patch->is_new = 1;
|
|
|
|
patch->is_delete = 0;
|
2019-07-09 00:33:06 +08:00
|
|
|
name = find_name_traditional(&state->root, second, NULL, state->p_value);
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->new_name = name;
|
|
|
|
} else if (is_dev_null(second)) {
|
|
|
|
patch->is_new = 0;
|
|
|
|
patch->is_delete = 1;
|
2019-07-09 00:33:06 +08:00
|
|
|
name = find_name_traditional(&state->root, first, NULL, state->p_value);
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->old_name = name;
|
|
|
|
} else {
|
|
|
|
char *first_name;
|
2019-07-09 00:33:06 +08:00
|
|
|
first_name = find_name_traditional(&state->root, first, NULL, state->p_value);
|
|
|
|
name = find_name_traditional(&state->root, second, first_name, state->p_value);
|
2016-04-23 02:55:46 +08:00
|
|
|
free(first_name);
|
|
|
|
if (has_epoch_timestamp(first)) {
|
|
|
|
patch->is_new = 1;
|
|
|
|
patch->is_delete = 0;
|
|
|
|
patch->new_name = name;
|
|
|
|
} else if (has_epoch_timestamp(second)) {
|
|
|
|
patch->is_new = 0;
|
|
|
|
patch->is_delete = 1;
|
|
|
|
patch->old_name = name;
|
|
|
|
} else {
|
|
|
|
patch->old_name = name;
|
|
|
|
patch->new_name = xstrdup_or_null(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!name)
|
|
|
|
return error(_("unable to find filename in patch at line %d"), state->linenr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-10-18 09:08:48 +08:00
|
|
|
static int gitdiff_hdrend(struct gitdiff_data *state UNUSED,
|
|
|
|
const char *line UNUSED,
|
|
|
|
struct patch *patch UNUSED)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're anal about diff header consistency, to make
|
|
|
|
* sure that we don't end up having strange ambiguous
|
|
|
|
* patches floating around.
|
|
|
|
*
|
|
|
|
* As a result, gitdiff_{old|new}name() will check
|
|
|
|
* their names against any previous information, just
|
|
|
|
* to make sure..
|
|
|
|
*/
|
|
|
|
#define DIFF_OLD_NAME 0
|
|
|
|
#define DIFF_NEW_NAME 1
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_verify_name(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
int isnull,
|
|
|
|
char **name,
|
|
|
|
int side)
|
|
|
|
{
|
|
|
|
if (!*name && !isnull) {
|
2019-07-12 00:08:43 +08:00
|
|
|
*name = find_name(state->root, line, NULL, state->p_value, TERM_TAB);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*name) {
|
|
|
|
char *another;
|
|
|
|
if (isnull)
|
|
|
|
return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
|
|
|
|
*name, state->linenr);
|
2019-07-12 00:08:43 +08:00
|
|
|
another = find_name(state->root, line, NULL, state->p_value, TERM_TAB);
|
2017-07-08 16:58:42 +08:00
|
|
|
if (!another || strcmp(another, *name)) {
|
2016-04-23 02:55:46 +08:00
|
|
|
free(another);
|
|
|
|
return error((side == DIFF_NEW_NAME) ?
|
|
|
|
_("git apply: bad git-diff - inconsistent new filename on line %d") :
|
|
|
|
_("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
|
|
|
|
}
|
|
|
|
free(another);
|
|
|
|
} else {
|
2018-02-15 08:29:34 +08:00
|
|
|
if (!is_dev_null(line))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_oldname(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
return gitdiff_verify_name(state, line,
|
|
|
|
patch->is_new, &patch->old_name,
|
|
|
|
DIFF_OLD_NAME);
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_newname(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
return gitdiff_verify_name(state, line,
|
|
|
|
patch->is_delete, &patch->new_name,
|
|
|
|
DIFF_NEW_NAME);
|
|
|
|
}
|
|
|
|
|
2017-06-28 01:03:47 +08:00
|
|
|
static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
*mode = strtoul(line, &end, 8);
|
|
|
|
if (end == line || !isspace(*end))
|
|
|
|
return error(_("invalid mode on line %d: %s"), linenr, line);
|
apply: canonicalize modes read from patches
Git stores only canonical modes for blobs. So for a regular file, we
care about only "100644" or "100755" (depending only on the executable
bit), but never modes where the group or other permissions are more
exotic. So never "100664", "100700", etc. When a file in the working
tree has such a mode, we quietly turn it into one of the two canonical
modes, and that's what is stored both in the index and in tree objects.
However, we don't canonicalize modes we read from incoming patches in
git-apply. These may appear in a few lines:
- "old mode" / "new mode" lines for mode changes
- "new file mode" lines for newly created files
- "deleted file mode" for removing files
For "new mode" and for "new file mode", this is harmless. The patch is
asking the result to have a certain mode, but:
- when we add an index entry (for --index or --cached), it is
canonicalized as we create the entry, via create_ce_mode().
- for a working tree file, try_create_file() passes either 0777 or
0666 to open(), so what you get depends only on your umask, not any
other bits (aside from the executable bit) in the original mode.
However, for "old mode" and "deleted file mode", there is a minor
annoyance. We compare the patch's expected preimage mode with the
current state. But that current state is always going to be a canonical
mode itself:
- updating an index entry via --cached will have the canonical mode in
the index
- for updating a working tree file, check_preimage() runs the mode
through ce_mode_from_stat(), which does the usual canonicalization
So if the patch feeds a non-canonical mode, it's impossible for it to
match, and we will always complain with something like:
file has type 100644, expected 100664
Since this is just a warning, the operation proceeds, but it's
confusing and annoying.
These cases should be pretty rare in practice. Git would never produce a
patch with non-canonical modes itself (since it doesn't store them).
And while we do accept patches from other programs, all of those lines
were invented by Git. So you'd need a program trying to be Git
compatible, but not handling canonicalization the same way. Reportedly
"quilt" is such a program.
We should canonicalize the modes as we read them so that the user never
sees the useless warning.
A few notes on the tests:
- I've covered instances of all lines for completeness, even though
the "new mode" / "new file mode" ones behave OK currently.
- the tests apply patches to both the index and working tree, and
check the result of both. Again, we know that all of these paths
canonicalize anyway, but it's giving us extra coverage (although we
are even less likely to have such a bug now since we canonicalize up
front).
- the test patches are missing "index" lines, which is also something
Git would never produce. But they don't matter for the test, they do
match the case from quilt we saw in the wild, and they avoid some
sha1/sha256 complexity.
Reported-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-05 14:00:10 +08:00
|
|
|
*mode = canon_mode(*mode);
|
2017-06-28 01:03:47 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_oldmode(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
2017-06-28 01:03:47 +08:00
|
|
|
return parse_mode_line(line, state->linenr, &patch->old_mode);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_newmode(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
2017-06-28 01:03:47 +08:00
|
|
|
return parse_mode_line(line, state->linenr, &patch->new_mode);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_delete(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
patch->is_delete = 1;
|
|
|
|
free(patch->old_name);
|
|
|
|
patch->old_name = xstrdup_or_null(patch->def_name);
|
|
|
|
return gitdiff_oldmode(state, line, patch);
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_newfile(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
patch->is_new = 1;
|
|
|
|
free(patch->new_name);
|
|
|
|
patch->new_name = xstrdup_or_null(patch->def_name);
|
|
|
|
return gitdiff_newmode(state, line, patch);
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_copysrc(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
patch->is_copy = 1;
|
|
|
|
free(patch->old_name);
|
2019-07-12 00:08:43 +08:00
|
|
|
patch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_copydst(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
patch->is_copy = 1;
|
|
|
|
free(patch->new_name);
|
2019-07-12 00:08:43 +08:00
|
|
|
patch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_renamesrc(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
patch->is_rename = 1;
|
|
|
|
free(patch->old_name);
|
2019-07-12 00:08:43 +08:00
|
|
|
patch->old_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_renamedst(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
patch->is_rename = 1;
|
|
|
|
free(patch->new_name);
|
2019-07-12 00:08:43 +08:00
|
|
|
patch->new_name = find_name(state->root, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-10-18 09:08:48 +08:00
|
|
|
static int gitdiff_similarity(struct gitdiff_data *state UNUSED,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
unsigned long val = strtoul(line, NULL, 10);
|
|
|
|
if (val <= 100)
|
|
|
|
patch->score = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-10-18 09:08:48 +08:00
|
|
|
static int gitdiff_dissimilarity(struct gitdiff_data *state UNUSED,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
unsigned long val = strtoul(line, NULL, 10);
|
|
|
|
if (val <= 100)
|
|
|
|
patch->score = val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:43 +08:00
|
|
|
static int gitdiff_index(struct gitdiff_data *state,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* index line is N hexadecimal, "..", N hexadecimal,
|
|
|
|
* and optional space with octal mode.
|
|
|
|
*/
|
|
|
|
const char *ptr, *eol;
|
|
|
|
int len;
|
2018-10-15 08:01:59 +08:00
|
|
|
const unsigned hexsz = the_hash_algo->hexsz;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
ptr = strchr(line, '.');
|
2018-10-15 08:01:59 +08:00
|
|
|
if (!ptr || ptr[1] != '.' || hexsz < ptr - line)
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
len = ptr - line;
|
2018-10-15 08:02:00 +08:00
|
|
|
memcpy(patch->old_oid_prefix, line, len);
|
|
|
|
patch->old_oid_prefix[len] = 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
line = ptr + 2;
|
|
|
|
ptr = strchr(line, ' ');
|
|
|
|
eol = strchrnul(line, '\n');
|
|
|
|
|
|
|
|
if (!ptr || eol < ptr)
|
|
|
|
ptr = eol;
|
|
|
|
len = ptr - line;
|
|
|
|
|
2018-10-15 08:01:59 +08:00
|
|
|
if (hexsz < len)
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
2018-10-15 08:02:00 +08:00
|
|
|
memcpy(patch->new_oid_prefix, line, len);
|
|
|
|
patch->new_oid_prefix[len] = 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
if (*ptr == ' ')
|
2017-06-28 01:03:47 +08:00
|
|
|
return gitdiff_oldmode(state, ptr + 1, patch);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is normal for a diff that doesn't change anything: we'll fall through
|
|
|
|
* into the next diff. Tell the parser to break out.
|
|
|
|
*/
|
2022-10-18 09:08:48 +08:00
|
|
|
static int gitdiff_unrecognized(struct gitdiff_data *state UNUSED,
|
|
|
|
const char *line UNUSED,
|
|
|
|
struct patch *patch UNUSED)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip p_value leading components from "line"; as we do not accept
|
|
|
|
* absolute paths, return NULL in that case.
|
|
|
|
*/
|
2019-07-09 00:33:03 +08:00
|
|
|
static const char *skip_tree_prefix(int p_value,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
int llen)
|
|
|
|
{
|
|
|
|
int nslash;
|
|
|
|
int i;
|
|
|
|
|
2019-07-09 00:33:03 +08:00
|
|
|
if (!p_value)
|
2016-04-23 02:55:46 +08:00
|
|
|
return (llen && line[0] == '/') ? NULL : line;
|
|
|
|
|
2019-07-09 00:33:03 +08:00
|
|
|
nslash = p_value;
|
2016-04-23 02:55:46 +08:00
|
|
|
for (i = 0; i < llen; i++) {
|
|
|
|
int ch = line[i];
|
|
|
|
if (ch == '/' && --nslash <= 0)
|
|
|
|
return (i == 0) ? NULL : &line[i + 1];
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is to extract the same name that appears on "diff --git"
|
|
|
|
* line. We do not find and return anything if it is a rename
|
|
|
|
* patch, and it is OK because we will find the name elsewhere.
|
|
|
|
* We need to reliably find name only when it is mode-change only,
|
|
|
|
* creation or deletion of an empty file. In any of these cases,
|
|
|
|
* both sides are the same name under a/ and b/ respectively.
|
|
|
|
*/
|
2019-07-09 00:33:04 +08:00
|
|
|
static char *git_header_name(int p_value,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *line,
|
|
|
|
int llen)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
const char *second = NULL;
|
|
|
|
size_t len, line_len;
|
|
|
|
|
|
|
|
line += strlen("diff --git ");
|
|
|
|
llen -= strlen("diff --git ");
|
|
|
|
|
|
|
|
if (*line == '"') {
|
|
|
|
const char *cp;
|
|
|
|
struct strbuf first = STRBUF_INIT;
|
|
|
|
struct strbuf sp = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (unquote_c_style(&first, line, &second))
|
|
|
|
goto free_and_fail1;
|
|
|
|
|
|
|
|
/* strip the a/b prefix including trailing slash */
|
2019-07-09 00:33:04 +08:00
|
|
|
cp = skip_tree_prefix(p_value, first.buf, first.len);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!cp)
|
|
|
|
goto free_and_fail1;
|
|
|
|
strbuf_remove(&first, 0, cp - first.buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* second points at one past closing dq of name.
|
|
|
|
* find the second name.
|
|
|
|
*/
|
|
|
|
while ((second < line + llen) && isspace(*second))
|
|
|
|
second++;
|
|
|
|
|
|
|
|
if (line + llen <= second)
|
|
|
|
goto free_and_fail1;
|
|
|
|
if (*second == '"') {
|
|
|
|
if (unquote_c_style(&sp, second, NULL))
|
|
|
|
goto free_and_fail1;
|
2019-07-09 00:33:04 +08:00
|
|
|
cp = skip_tree_prefix(p_value, sp.buf, sp.len);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!cp)
|
|
|
|
goto free_and_fail1;
|
|
|
|
/* They must match, otherwise ignore */
|
|
|
|
if (strcmp(cp, first.buf))
|
|
|
|
goto free_and_fail1;
|
|
|
|
strbuf_release(&sp);
|
|
|
|
return strbuf_detach(&first, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unquoted second */
|
2019-07-09 00:33:04 +08:00
|
|
|
cp = skip_tree_prefix(p_value, second, line + llen - second);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!cp)
|
|
|
|
goto free_and_fail1;
|
|
|
|
if (line + llen - cp != first.len ||
|
|
|
|
memcmp(first.buf, cp, first.len))
|
|
|
|
goto free_and_fail1;
|
|
|
|
return strbuf_detach(&first, NULL);
|
|
|
|
|
|
|
|
free_and_fail1:
|
|
|
|
strbuf_release(&first);
|
|
|
|
strbuf_release(&sp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unquoted first name */
|
2019-07-09 00:33:04 +08:00
|
|
|
name = skip_tree_prefix(p_value, line, llen);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!name)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* since the first name is unquoted, a dq if exists must be
|
|
|
|
* the beginning of the second name.
|
|
|
|
*/
|
|
|
|
for (second = name; second < line + llen; second++) {
|
|
|
|
if (*second == '"') {
|
|
|
|
struct strbuf sp = STRBUF_INIT;
|
|
|
|
const char *np;
|
|
|
|
|
|
|
|
if (unquote_c_style(&sp, second, NULL))
|
|
|
|
goto free_and_fail2;
|
|
|
|
|
2019-07-09 00:33:04 +08:00
|
|
|
np = skip_tree_prefix(p_value, sp.buf, sp.len);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!np)
|
|
|
|
goto free_and_fail2;
|
|
|
|
|
|
|
|
len = sp.buf + sp.len - np;
|
|
|
|
if (len < second - name &&
|
|
|
|
!strncmp(np, name, len) &&
|
|
|
|
isspace(name[len])) {
|
|
|
|
/* Good */
|
|
|
|
strbuf_remove(&sp, 0, np - sp.buf);
|
|
|
|
return strbuf_detach(&sp, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_and_fail2:
|
|
|
|
strbuf_release(&sp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Accept a name only if it shows up twice, exactly the same
|
|
|
|
* form.
|
|
|
|
*/
|
|
|
|
second = strchr(name, '\n');
|
|
|
|
if (!second)
|
|
|
|
return NULL;
|
|
|
|
line_len = second - name;
|
|
|
|
for (len = 0 ; ; len++) {
|
|
|
|
switch (name[len]) {
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
case '\n':
|
|
|
|
return NULL;
|
|
|
|
case '\t': case ' ':
|
|
|
|
/*
|
|
|
|
* Is this the separator between the preimage
|
|
|
|
* and the postimage pathname? Again, we are
|
|
|
|
* only interested in the case where there is
|
|
|
|
* no rename, as this is only to set def_name
|
|
|
|
* and a rename patch has the names elsewhere
|
|
|
|
* in an unambiguous form.
|
|
|
|
*/
|
|
|
|
if (!name[len + 1])
|
|
|
|
return NULL; /* no postimage name */
|
2019-07-09 00:33:04 +08:00
|
|
|
second = skip_tree_prefix(p_value, name + len + 1,
|
2016-04-23 02:55:46 +08:00
|
|
|
line_len - (len + 1));
|
apply: parse names out of "diff --git" more carefully
"git apply" uses the pathname parsed out of the "diff --git" header
to decide which path is being patched, but this is used only when
there is no other names available in the patch. When there is any
content change (like we can see in this patch, that modifies the
contents of "apply.c") or rename (which comes with "rename from" and
"rename to" extended diff headers), the names are available without
having to parse this header.
When we do need to parse this header, a special care needs to be
taken, as the name of a directory or a file can have a SP in it so
it is not like "find a space, and take everything before the space
and that is the preimage filename, everything after the space is the
postimage filename". We have a loop that stops at every SP on the
"diff --git a/dir/file b/dir/foo" line and see if that SP is the
right place that separates such a pair of names.
Unfortunately, this loop can terminate prematurely when a crafted
directory name ended with a SP. The next pathname component after
that SP (i.e. the beginning of the possible postimage filename) will
be a slash, and instead of rejecting that position as the valid
separation point between pre- and post-image filenames and keep
looping, we stopped processing right there.
The fix is simple. Instead of stopping and giving up, keep going on
when we see such a condition.
Reported-by: Han Young <hanyang.tony@bytedance.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-20 06:56:44 +08:00
|
|
|
/*
|
|
|
|
* If we are at the SP at the end of a directory,
|
|
|
|
* skip_tree_prefix() may return NULL as that makes
|
|
|
|
* it appears as if we have an absolute path.
|
|
|
|
* Keep going to find another SP.
|
|
|
|
*/
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!second)
|
apply: parse names out of "diff --git" more carefully
"git apply" uses the pathname parsed out of the "diff --git" header
to decide which path is being patched, but this is used only when
there is no other names available in the patch. When there is any
content change (like we can see in this patch, that modifies the
contents of "apply.c") or rename (which comes with "rename from" and
"rename to" extended diff headers), the names are available without
having to parse this header.
When we do need to parse this header, a special care needs to be
taken, as the name of a directory or a file can have a SP in it so
it is not like "find a space, and take everything before the space
and that is the preimage filename, everything after the space is the
postimage filename". We have a loop that stops at every SP on the
"diff --git a/dir/file b/dir/foo" line and see if that SP is the
right place that separates such a pair of names.
Unfortunately, this loop can terminate prematurely when a crafted
directory name ended with a SP. The next pathname component after
that SP (i.e. the beginning of the possible postimage filename) will
be a slash, and instead of rejecting that position as the valid
separation point between pre- and post-image filenames and keep
looping, we stopped processing right there.
The fix is simple. Instead of stopping and giving up, keep going on
when we see such a condition.
Reported-by: Han Young <hanyang.tony@bytedance.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-20 06:56:44 +08:00
|
|
|
continue;
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* Does len bytes starting at "name" and "second"
|
|
|
|
* (that are separated by one HT or SP we just
|
|
|
|
* found) exactly match?
|
|
|
|
*/
|
|
|
|
if (second[len] == '\n' && !strncmp(name, second, len))
|
|
|
|
return xmemdupz(name, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:33:05 +08:00
|
|
|
static int check_header_line(int linenr, struct patch *patch)
|
2017-06-28 01:03:39 +08:00
|
|
|
{
|
|
|
|
int extensions = (patch->is_delete == 1) + (patch->is_new == 1) +
|
|
|
|
(patch->is_rename == 1) + (patch->is_copy == 1);
|
|
|
|
if (extensions > 1)
|
|
|
|
return error(_("inconsistent header lines %d and %d"),
|
2019-07-09 00:33:05 +08:00
|
|
|
patch->extension_linenr, linenr);
|
2017-06-28 01:03:39 +08:00
|
|
|
if (extensions && !patch->extension_linenr)
|
2019-07-09 00:33:05 +08:00
|
|
|
patch->extension_linenr = linenr;
|
2017-06-28 01:03:39 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 00:08:44 +08:00
|
|
|
int parse_git_diff_header(struct strbuf *root,
|
|
|
|
int *linenr,
|
|
|
|
int p_value,
|
|
|
|
const char *line,
|
|
|
|
int len,
|
|
|
|
unsigned int size,
|
|
|
|
struct patch *patch)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
unsigned long offset;
|
2019-07-12 00:08:43 +08:00
|
|
|
struct gitdiff_data parse_hdr_state;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* A git diff has explicit new/delete information, so we don't guess */
|
|
|
|
patch->is_new = 0;
|
|
|
|
patch->is_delete = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some things may not have the old name in the
|
|
|
|
* rest of the headers anywhere (pure mode changes,
|
|
|
|
* or removing or adding empty files), so we get
|
|
|
|
* the default name from the header.
|
|
|
|
*/
|
2019-07-12 00:08:44 +08:00
|
|
|
patch->def_name = git_header_name(p_value, line, len);
|
|
|
|
if (patch->def_name && root->len) {
|
|
|
|
char *s = xstrfmt("%s%s", root->buf, patch->def_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
free(patch->def_name);
|
|
|
|
patch->def_name = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
line += len;
|
|
|
|
size -= len;
|
2019-07-12 00:08:44 +08:00
|
|
|
(*linenr)++;
|
|
|
|
parse_hdr_state.root = root;
|
|
|
|
parse_hdr_state.linenr = *linenr;
|
|
|
|
parse_hdr_state.p_value = p_value;
|
2019-07-12 00:08:43 +08:00
|
|
|
|
2019-07-12 00:08:44 +08:00
|
|
|
for (offset = len ; size > 0 ; offset += len, size -= len, line += len, (*linenr)++) {
|
2016-04-23 02:55:46 +08:00
|
|
|
static const struct opentry {
|
|
|
|
const char *str;
|
2019-07-12 00:08:43 +08:00
|
|
|
int (*fn)(struct gitdiff_data *, const char *, struct patch *);
|
2016-04-23 02:55:46 +08:00
|
|
|
} optable[] = {
|
|
|
|
{ "@@ -", gitdiff_hdrend },
|
|
|
|
{ "--- ", gitdiff_oldname },
|
|
|
|
{ "+++ ", gitdiff_newname },
|
|
|
|
{ "old mode ", gitdiff_oldmode },
|
|
|
|
{ "new mode ", gitdiff_newmode },
|
|
|
|
{ "deleted file mode ", gitdiff_delete },
|
|
|
|
{ "new file mode ", gitdiff_newfile },
|
|
|
|
{ "copy from ", gitdiff_copysrc },
|
|
|
|
{ "copy to ", gitdiff_copydst },
|
|
|
|
{ "rename old ", gitdiff_renamesrc },
|
|
|
|
{ "rename new ", gitdiff_renamedst },
|
|
|
|
{ "rename from ", gitdiff_renamesrc },
|
|
|
|
{ "rename to ", gitdiff_renamedst },
|
|
|
|
{ "similarity index ", gitdiff_similarity },
|
|
|
|
{ "dissimilarity index ", gitdiff_dissimilarity },
|
|
|
|
{ "index ", gitdiff_index },
|
|
|
|
{ "", gitdiff_unrecognized },
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
|
|
|
|
len = linelen(line, size);
|
|
|
|
if (!len || line[len-1] != '\n')
|
|
|
|
break;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(optable); i++) {
|
|
|
|
const struct opentry *p = optable + i;
|
|
|
|
int oplen = strlen(p->str);
|
|
|
|
int res;
|
|
|
|
if (len < oplen || memcmp(p->str, line, oplen))
|
|
|
|
continue;
|
2019-07-12 00:08:43 +08:00
|
|
|
res = p->fn(&parse_hdr_state, line + oplen, patch);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res < 0)
|
|
|
|
return -1;
|
2019-07-12 00:08:44 +08:00
|
|
|
if (check_header_line(*linenr, patch))
|
2017-06-28 01:03:39 +08:00
|
|
|
return -1;
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res > 0)
|
2019-10-09 01:38:43 +08:00
|
|
|
goto done;
|
2016-04-23 02:55:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-09 01:38:43 +08:00
|
|
|
done:
|
|
|
|
if (!patch->old_name && !patch->new_name) {
|
|
|
|
if (!patch->def_name) {
|
|
|
|
error(Q_("git diff header lacks filename information when removing "
|
|
|
|
"%d leading pathname component (line %d)",
|
|
|
|
"git diff header lacks filename information when removing "
|
|
|
|
"%d leading pathname components (line %d)",
|
|
|
|
parse_hdr_state.p_value),
|
|
|
|
parse_hdr_state.p_value, *linenr);
|
|
|
|
return -128;
|
|
|
|
}
|
|
|
|
patch->old_name = xstrdup(patch->def_name);
|
|
|
|
patch->new_name = xstrdup(patch->def_name);
|
|
|
|
}
|
|
|
|
if ((!patch->new_name && !patch->is_delete) ||
|
|
|
|
(!patch->old_name && !patch->is_new)) {
|
|
|
|
error(_("git diff header lacks filename information "
|
|
|
|
"(line %d)"), *linenr);
|
|
|
|
return -128;
|
|
|
|
}
|
|
|
|
patch->is_toplevel_relative = 1;
|
2016-04-23 02:55:46 +08:00
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_num(const char *line, unsigned long *p)
|
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
if (!isdigit(*line))
|
|
|
|
return 0;
|
|
|
|
*p = strtoul(line, &ptr, 10);
|
|
|
|
return ptr - line;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_range(const char *line, int len, int offset, const char *expect,
|
|
|
|
unsigned long *p1, unsigned long *p2)
|
|
|
|
{
|
|
|
|
int digits, ex;
|
|
|
|
|
|
|
|
if (offset < 0 || offset >= len)
|
|
|
|
return -1;
|
|
|
|
line += offset;
|
|
|
|
len -= offset;
|
|
|
|
|
|
|
|
digits = parse_num(line, p1);
|
|
|
|
if (!digits)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
offset += digits;
|
|
|
|
line += digits;
|
|
|
|
len -= digits;
|
|
|
|
|
|
|
|
*p2 = 1;
|
|
|
|
if (*line == ',') {
|
|
|
|
digits = parse_num(line+1, p2);
|
|
|
|
if (!digits)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
offset += digits+1;
|
|
|
|
line += digits+1;
|
|
|
|
len -= digits+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ex = strlen(expect);
|
|
|
|
if (ex > len)
|
|
|
|
return -1;
|
|
|
|
if (memcmp(line, expect, ex))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return offset + ex;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void recount_diff(const char *line, int size, struct fragment *fragment)
|
|
|
|
{
|
|
|
|
int oldlines = 0, newlines = 0, ret = 0;
|
|
|
|
|
|
|
|
if (size < 1) {
|
|
|
|
warning("recount: ignore empty hunk");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int len = linelen(line, size);
|
|
|
|
size -= len;
|
|
|
|
line += len;
|
|
|
|
|
|
|
|
if (size < 1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (*line) {
|
|
|
|
case ' ': case '\n':
|
|
|
|
newlines++;
|
|
|
|
/* fall through */
|
|
|
|
case '-':
|
|
|
|
oldlines++;
|
|
|
|
continue;
|
|
|
|
case '+':
|
|
|
|
newlines++;
|
|
|
|
continue;
|
|
|
|
case '\\':
|
|
|
|
continue;
|
|
|
|
case '@':
|
|
|
|
ret = size < 3 || !starts_with(line, "@@ ");
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
ret = size < 5 || !starts_with(line, "diff ");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ret) {
|
|
|
|
warning(_("recount: unexpected line: %.*s"),
|
|
|
|
(int)linelen(line, size), line);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fragment->oldlines = oldlines;
|
|
|
|
fragment->newlines = newlines;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse a unified diff fragment header of the
|
|
|
|
* form "@@ -a,b +c,d @@"
|
|
|
|
*/
|
|
|
|
static int parse_fragment_header(const char *line, int len, struct fragment *fragment)
|
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
if (!len || line[len-1] != '\n')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Figure out the number of lines in a fragment */
|
|
|
|
offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines);
|
|
|
|
offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines);
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find file diff header
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* -1 if no header was found
|
|
|
|
* -128 in case of error
|
|
|
|
* the size of the header in bytes (called "offset") otherwise
|
|
|
|
*/
|
|
|
|
static int find_header(struct apply_state *state,
|
|
|
|
const char *line,
|
|
|
|
unsigned long size,
|
|
|
|
int *hdrsize,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
unsigned long offset, len;
|
|
|
|
|
|
|
|
patch->is_toplevel_relative = 0;
|
|
|
|
patch->is_rename = patch->is_copy = 0;
|
|
|
|
patch->is_new = patch->is_delete = -1;
|
|
|
|
patch->old_mode = patch->new_mode = 0;
|
|
|
|
patch->old_name = patch->new_name = NULL;
|
|
|
|
for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) {
|
|
|
|
unsigned long nextlen;
|
|
|
|
|
|
|
|
len = linelen(line, size);
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Testing this early allows us to take a few shortcuts.. */
|
|
|
|
if (len < 6)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we don't find any unconnected patch fragments.
|
|
|
|
* That's a sign that we didn't find a header, and that a
|
|
|
|
* patch has become corrupted/broken up.
|
|
|
|
*/
|
|
|
|
if (!memcmp("@@ -", line, 4)) {
|
|
|
|
struct fragment dummy;
|
|
|
|
if (parse_fragment_header(line, len, &dummy) < 0)
|
|
|
|
continue;
|
|
|
|
error(_("patch fragment without header at line %d: %.*s"),
|
|
|
|
state->linenr, (int)len-1, line);
|
|
|
|
return -128;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size < len + 6)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Git patch? It might not have a real patch, just a rename
|
|
|
|
* or mode change, so we handle that specially
|
|
|
|
*/
|
|
|
|
if (!memcmp("diff --git ", line, 11)) {
|
2019-07-12 00:08:44 +08:00
|
|
|
int git_hdr_len = parse_git_diff_header(&state->root, &state->linenr,
|
|
|
|
state->p_value, line, len,
|
|
|
|
size, patch);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (git_hdr_len < 0)
|
|
|
|
return -128;
|
|
|
|
if (git_hdr_len <= len)
|
|
|
|
continue;
|
|
|
|
*hdrsize = git_hdr_len;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- followed by +++ ? */
|
|
|
|
if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only accept unified patches, so we want it to
|
|
|
|
* at least have "@@ -a,b +c,d @@\n", which is 14 chars
|
|
|
|
* minimum ("@@ -0,0 +1 @@\n" is the shortest).
|
|
|
|
*/
|
|
|
|
nextlen = linelen(line + len, size - len);
|
|
|
|
if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Ok, we'll consider it a patch */
|
|
|
|
if (parse_traditional_patch(state, line, line+len, patch))
|
|
|
|
return -128;
|
|
|
|
*hdrsize = len + nextlen;
|
|
|
|
state->linenr += 2;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void record_ws_error(struct apply_state *state,
|
|
|
|
unsigned result,
|
|
|
|
const char *line,
|
|
|
|
int len,
|
|
|
|
int linenr)
|
|
|
|
{
|
|
|
|
char *err;
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
return;
|
|
|
|
|
|
|
|
state->whitespace_error++;
|
|
|
|
if (state->squelch_whitespace_errors &&
|
|
|
|
state->squelch_whitespace_errors < state->whitespace_error)
|
|
|
|
return;
|
|
|
|
|
|
|
|
err = whitespace_error_string(result);
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
|
|
|
|
state->patch_input_file, linenr, err, len, line);
|
2016-04-23 02:55:46 +08:00
|
|
|
free(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void check_whitespace(struct apply_state *state,
|
|
|
|
const char *line,
|
|
|
|
int len,
|
|
|
|
unsigned ws_rule)
|
|
|
|
{
|
|
|
|
unsigned result = ws_check(line + 1, len - 1, ws_rule);
|
|
|
|
|
|
|
|
record_ws_error(state, result, line + 1, len - 2, state->linenr);
|
|
|
|
}
|
|
|
|
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
/*
|
|
|
|
* Check if the patch has context lines with CRLF or
|
|
|
|
* the patch wants to remove lines with CRLF.
|
|
|
|
*/
|
|
|
|
static void check_old_for_crlf(struct patch *patch, const char *line, int len)
|
|
|
|
{
|
|
|
|
if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
|
|
|
|
patch->ws_rule |= WS_CR_AT_EOL;
|
|
|
|
patch->crlf_in_old = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* Parse a unified diff. Note that this really needs to parse each
|
|
|
|
* fragment separately, since the only way to know the difference
|
|
|
|
* between a "---" that is part of a patch, and a "---" that starts
|
|
|
|
* the next patch is to look at the line counts..
|
|
|
|
*/
|
|
|
|
static int parse_fragment(struct apply_state *state,
|
|
|
|
const char *line,
|
|
|
|
unsigned long size,
|
|
|
|
struct patch *patch,
|
|
|
|
struct fragment *fragment)
|
|
|
|
{
|
|
|
|
int added, deleted;
|
|
|
|
int len = linelen(line, size), offset;
|
|
|
|
unsigned long oldlines, newlines;
|
|
|
|
unsigned long leading, trailing;
|
|
|
|
|
|
|
|
offset = parse_fragment_header(line, len, fragment);
|
|
|
|
if (offset < 0)
|
|
|
|
return -1;
|
|
|
|
if (offset > 0 && patch->recount)
|
|
|
|
recount_diff(line + offset, size - offset, fragment);
|
|
|
|
oldlines = fragment->oldlines;
|
|
|
|
newlines = fragment->newlines;
|
|
|
|
leading = 0;
|
|
|
|
trailing = 0;
|
|
|
|
|
|
|
|
/* Parse the thing.. */
|
|
|
|
line += len;
|
|
|
|
size -= len;
|
|
|
|
state->linenr++;
|
|
|
|
added = deleted = 0;
|
|
|
|
for (offset = len;
|
|
|
|
0 < size;
|
|
|
|
offset += len, size -= len, line += len, state->linenr++) {
|
|
|
|
if (!oldlines && !newlines)
|
|
|
|
break;
|
|
|
|
len = linelen(line, size);
|
|
|
|
if (!len || line[len-1] != '\n')
|
|
|
|
return -1;
|
|
|
|
switch (*line) {
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
case '\n': /* newer GNU diff, an empty context line */
|
|
|
|
case ' ':
|
|
|
|
oldlines--;
|
|
|
|
newlines--;
|
|
|
|
if (!deleted && !added)
|
|
|
|
leading++;
|
|
|
|
trailing++;
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
check_old_for_crlf(patch, line, len);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!state->apply_in_reverse &&
|
|
|
|
state->ws_error_action == correct_ws_error)
|
|
|
|
check_whitespace(state, line, len, patch->ws_rule);
|
|
|
|
break;
|
|
|
|
case '-':
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
if (!state->apply_in_reverse)
|
|
|
|
check_old_for_crlf(patch, line, len);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (state->apply_in_reverse &&
|
|
|
|
state->ws_error_action != nowarn_ws_error)
|
|
|
|
check_whitespace(state, line, len, patch->ws_rule);
|
|
|
|
deleted++;
|
|
|
|
oldlines--;
|
|
|
|
trailing = 0;
|
|
|
|
break;
|
|
|
|
case '+':
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
if (state->apply_in_reverse)
|
|
|
|
check_old_for_crlf(patch, line, len);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!state->apply_in_reverse &&
|
|
|
|
state->ws_error_action != nowarn_ws_error)
|
|
|
|
check_whitespace(state, line, len, patch->ws_rule);
|
|
|
|
added++;
|
|
|
|
newlines--;
|
|
|
|
trailing = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We allow "\ No newline at end of file". Depending
|
|
|
|
* on locale settings when the patch was produced we
|
|
|
|
* don't know what this line looks like. The only
|
|
|
|
* thing we do know is that it begins with "\ ".
|
|
|
|
* Checking for 12 is just for sanity check -- any
|
|
|
|
* l10n of "\ No newline..." is at least that long.
|
|
|
|
*/
|
|
|
|
case '\\':
|
|
|
|
if (len < 12 || memcmp(line, "\\ ", 2))
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (oldlines || newlines)
|
|
|
|
return -1;
|
2018-11-13 04:54:49 +08:00
|
|
|
if (!patch->recount && !deleted && !added)
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
fragment->leading = leading;
|
|
|
|
fragment->trailing = trailing;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a fragment ends with an incomplete line, we failed to include
|
|
|
|
* it in the above loop because we hit oldlines == newlines == 0
|
|
|
|
* before seeing it.
|
|
|
|
*/
|
|
|
|
if (12 < size && !memcmp(line, "\\ ", 2))
|
|
|
|
offset += linelen(line, size);
|
|
|
|
|
|
|
|
patch->lines_added += added;
|
|
|
|
patch->lines_deleted += deleted;
|
|
|
|
|
|
|
|
if (0 < patch->is_new && oldlines)
|
|
|
|
return error(_("new file depends on old contents"));
|
|
|
|
if (0 < patch->is_delete && newlines)
|
|
|
|
return error(_("deleted file still has contents"));
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have seen "diff --git a/... b/..." header (or a traditional patch
|
|
|
|
* header). Read hunks that belong to this patch into fragments and hang
|
|
|
|
* them to the given patch structure.
|
|
|
|
*
|
|
|
|
* The (fragment->patch, fragment->size) pair points into the memory given
|
|
|
|
* by the caller, not a copy, when we return.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* -1 in case of error,
|
|
|
|
* the number of bytes in the patch otherwise.
|
|
|
|
*/
|
|
|
|
static int parse_single_patch(struct apply_state *state,
|
|
|
|
const char *line,
|
|
|
|
unsigned long size,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
unsigned long offset = 0;
|
|
|
|
unsigned long oldlines = 0, newlines = 0, context = 0;
|
|
|
|
struct fragment **fragp = &patch->fragments;
|
|
|
|
|
|
|
|
while (size > 4 && !memcmp(line, "@@ -", 4)) {
|
|
|
|
struct fragment *fragment;
|
|
|
|
int len;
|
|
|
|
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(fragment, 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
fragment->linenr = state->linenr;
|
|
|
|
len = parse_fragment(state, line, size, patch, fragment);
|
|
|
|
if (len <= 0) {
|
|
|
|
free(fragment);
|
|
|
|
return error(_("corrupt patch at line %d"), state->linenr);
|
|
|
|
}
|
|
|
|
fragment->patch = line;
|
|
|
|
fragment->size = len;
|
|
|
|
oldlines += fragment->oldlines;
|
|
|
|
newlines += fragment->newlines;
|
|
|
|
context += fragment->leading + fragment->trailing;
|
|
|
|
|
|
|
|
*fragp = fragment;
|
|
|
|
fragp = &fragment->next;
|
|
|
|
|
|
|
|
offset += len;
|
|
|
|
line += len;
|
|
|
|
size -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If something was removed (i.e. we have old-lines) it cannot
|
|
|
|
* be creation, and if something was added it cannot be
|
|
|
|
* deletion. However, the reverse is not true; --unified=0
|
|
|
|
* patches that only add are not necessarily creation even
|
|
|
|
* though they do not have any old lines, and ones that only
|
|
|
|
* delete are not necessarily deletion.
|
|
|
|
*
|
|
|
|
* Unfortunately, a real creation/deletion patch do _not_ have
|
|
|
|
* any context line by definition, so we cannot safely tell it
|
|
|
|
* apart with --unified=0 insanity. At least if the patch has
|
|
|
|
* more than one hunk it is not creation or deletion.
|
|
|
|
*/
|
|
|
|
if (patch->is_new < 0 &&
|
|
|
|
(oldlines || (patch->fragments && patch->fragments->next)))
|
|
|
|
patch->is_new = 0;
|
|
|
|
if (patch->is_delete < 0 &&
|
|
|
|
(newlines || (patch->fragments && patch->fragments->next)))
|
|
|
|
patch->is_delete = 0;
|
|
|
|
|
|
|
|
if (0 < patch->is_new && oldlines)
|
|
|
|
return error(_("new file %s depends on old contents"), patch->new_name);
|
|
|
|
if (0 < patch->is_delete && newlines)
|
|
|
|
return error(_("deleted file %s still has contents"), patch->old_name);
|
2016-09-05 04:18:25 +08:00
|
|
|
if (!patch->is_delete && !newlines && context && state->apply_verbosity > verbosity_silent)
|
2016-04-23 02:55:46 +08:00
|
|
|
fprintf_ln(stderr,
|
|
|
|
_("** warning: "
|
|
|
|
"file %s becomes empty but is not deleted"),
|
|
|
|
patch->new_name);
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int metadata_changes(struct patch *patch)
|
|
|
|
{
|
|
|
|
return patch->is_rename > 0 ||
|
|
|
|
patch->is_copy > 0 ||
|
|
|
|
patch->is_new > 0 ||
|
|
|
|
patch->is_delete ||
|
|
|
|
(patch->old_mode && patch->new_mode &&
|
|
|
|
patch->old_mode != patch->new_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *inflate_it(const void *data, unsigned long size,
|
|
|
|
unsigned long inflated_size)
|
|
|
|
{
|
|
|
|
git_zstream stream;
|
|
|
|
void *out;
|
|
|
|
int st;
|
|
|
|
|
|
|
|
memset(&stream, 0, sizeof(stream));
|
|
|
|
|
|
|
|
stream.next_in = (unsigned char *)data;
|
|
|
|
stream.avail_in = size;
|
|
|
|
stream.next_out = out = xmalloc(inflated_size);
|
|
|
|
stream.avail_out = inflated_size;
|
|
|
|
git_inflate_init(&stream);
|
|
|
|
st = git_inflate(&stream, Z_FINISH);
|
|
|
|
git_inflate_end(&stream);
|
|
|
|
if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
|
|
|
|
free(out);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a binary hunk and return a new fragment; fragment->patch
|
|
|
|
* points at an allocated memory that the caller must free, so
|
|
|
|
* it is marked as "->free_patch = 1".
|
|
|
|
*/
|
|
|
|
static struct fragment *parse_binary_hunk(struct apply_state *state,
|
|
|
|
char **buf_p,
|
|
|
|
unsigned long *sz_p,
|
|
|
|
int *status_p,
|
|
|
|
int *used_p)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Expect a line that begins with binary patch method ("literal"
|
|
|
|
* or "delta"), followed by the length of data before deflating.
|
|
|
|
* a sequence of 'length-byte' followed by base-85 encoded data
|
|
|
|
* should follow, terminated by a newline.
|
|
|
|
*
|
|
|
|
* Each 5-byte sequence of base-85 encodes up to 4 bytes,
|
|
|
|
* and we would limit the patch line to 66 characters,
|
|
|
|
* so one line can fit up to 13 groups that would decode
|
|
|
|
* to 52 bytes max. The length byte 'A'-'Z' corresponds
|
|
|
|
* to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
|
|
|
|
*/
|
|
|
|
int llen, used;
|
|
|
|
unsigned long size = *sz_p;
|
|
|
|
char *buffer = *buf_p;
|
|
|
|
int patch_method;
|
|
|
|
unsigned long origlen;
|
|
|
|
char *data = NULL;
|
|
|
|
int hunk_size = 0;
|
|
|
|
struct fragment *frag;
|
|
|
|
|
|
|
|
llen = linelen(buffer, size);
|
|
|
|
used = llen;
|
|
|
|
|
|
|
|
*status_p = 0;
|
|
|
|
|
|
|
|
if (starts_with(buffer, "delta ")) {
|
|
|
|
patch_method = BINARY_DELTA_DEFLATED;
|
|
|
|
origlen = strtoul(buffer + 6, NULL, 10);
|
|
|
|
}
|
|
|
|
else if (starts_with(buffer, "literal ")) {
|
|
|
|
patch_method = BINARY_LITERAL_DEFLATED;
|
|
|
|
origlen = strtoul(buffer + 8, NULL, 10);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
state->linenr++;
|
|
|
|
buffer += llen;
|
apply: keep buffer/size pair in sync when parsing binary hunks
We parse through binary hunks by looping through the buffer with code
like:
llen = linelen(buffer, size);
...do something with the line...
buffer += llen;
size -= llen;
However, before we enter the loop, there is one call that increments
"buffer" but forgets to decrement "size". As a result, our "size" is off
by the length of that line, and subsequent calls to linelen() may look
past the end of the buffer for a newline.
The fix is easy: we just need to decrement size as we do elsewhere.
This bug goes all the way back to 0660626caf (binary diff: further
updates., 2006-05-05). Presumably nobody noticed because it only
triggers if the patch is corrupted, and even then we are often "saved"
by luck. We use a strbuf to store the incoming patch, so we overallocate
there, plus we add a 16-byte run of NULs as slop for memory comparisons.
So if this happened accidentally, the common case is that we'd just read
a few uninitialized bytes from the end of the strbuf before producing
the expected "this patch is corrupted" error complaint.
However, it is possible to carefully construct a case which reads off
the end of the buffer. The included test does so. It will pass both
before and after this patch when run normally, but using a tool like
ASan shows that we get an out-of-bounds read before this patch, but not
after.
Reported-by: Xingman Chen <xichixingman@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-08-10 09:01:52 +08:00
|
|
|
size -= llen;
|
2016-04-23 02:55:46 +08:00
|
|
|
while (1) {
|
|
|
|
int byte_length, max_byte_length, newsize;
|
|
|
|
llen = linelen(buffer, size);
|
|
|
|
used += llen;
|
|
|
|
state->linenr++;
|
|
|
|
if (llen == 1) {
|
|
|
|
/* consume the blank line */
|
|
|
|
buffer++;
|
|
|
|
size--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Minimum line is "A00000\n" which is 7-byte long,
|
|
|
|
* and the line length must be multiple of 5 plus 2.
|
|
|
|
*/
|
|
|
|
if ((llen < 7) || (llen-2) % 5)
|
|
|
|
goto corrupt;
|
|
|
|
max_byte_length = (llen - 2) / 5 * 4;
|
|
|
|
byte_length = *buffer;
|
|
|
|
if ('A' <= byte_length && byte_length <= 'Z')
|
|
|
|
byte_length = byte_length - 'A' + 1;
|
|
|
|
else if ('a' <= byte_length && byte_length <= 'z')
|
|
|
|
byte_length = byte_length - 'a' + 27;
|
|
|
|
else
|
|
|
|
goto corrupt;
|
|
|
|
/* if the input length was not multiple of 4, we would
|
|
|
|
* have filler at the end but the filler should never
|
|
|
|
* exceed 3 bytes
|
|
|
|
*/
|
|
|
|
if (max_byte_length < byte_length ||
|
|
|
|
byte_length <= max_byte_length - 4)
|
|
|
|
goto corrupt;
|
|
|
|
newsize = hunk_size + byte_length;
|
|
|
|
data = xrealloc(data, newsize);
|
|
|
|
if (decode_85(data + hunk_size, buffer + 1, byte_length))
|
|
|
|
goto corrupt;
|
|
|
|
hunk_size = newsize;
|
|
|
|
buffer += llen;
|
|
|
|
size -= llen;
|
|
|
|
}
|
|
|
|
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(frag, 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
frag->patch = inflate_it(data, hunk_size, origlen);
|
|
|
|
frag->free_patch = 1;
|
|
|
|
if (!frag->patch)
|
|
|
|
goto corrupt;
|
|
|
|
free(data);
|
|
|
|
frag->size = origlen;
|
|
|
|
*buf_p = buffer;
|
|
|
|
*sz_p = size;
|
|
|
|
*used_p = used;
|
|
|
|
frag->binary_patch_method = patch_method;
|
|
|
|
return frag;
|
|
|
|
|
|
|
|
corrupt:
|
|
|
|
free(data);
|
|
|
|
*status_p = -1;
|
|
|
|
error(_("corrupt binary patch at line %d: %.*s"),
|
|
|
|
state->linenr-1, llen-1, buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns:
|
|
|
|
* -1 in case of error,
|
|
|
|
* the length of the parsed binary patch otherwise
|
|
|
|
*/
|
|
|
|
static int parse_binary(struct apply_state *state,
|
|
|
|
char *buffer,
|
|
|
|
unsigned long size,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We have read "GIT binary patch\n"; what follows is a line
|
|
|
|
* that says the patch method (currently, either "literal" or
|
|
|
|
* "delta") and the length of data before deflating; a
|
|
|
|
* sequence of 'length-byte' followed by base-85 encoded data
|
|
|
|
* follows.
|
|
|
|
*
|
|
|
|
* When a binary patch is reversible, there is another binary
|
|
|
|
* hunk in the same format, starting with patch method (either
|
|
|
|
* "literal" or "delta") with the length of data, and a sequence
|
|
|
|
* of length-byte + base-85 encoded data, terminated with another
|
|
|
|
* empty line. This data, when applied to the postimage, produces
|
|
|
|
* the preimage.
|
|
|
|
*/
|
|
|
|
struct fragment *forward;
|
|
|
|
struct fragment *reverse;
|
|
|
|
int status;
|
|
|
|
int used, used_1;
|
|
|
|
|
|
|
|
forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
|
|
|
|
if (!forward && !status)
|
|
|
|
/* there has to be one hunk (forward hunk) */
|
|
|
|
return error(_("unrecognized binary patch at line %d"), state->linenr-1);
|
|
|
|
if (status)
|
|
|
|
/* otherwise we already gave an error message */
|
|
|
|
return status;
|
|
|
|
|
|
|
|
reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1);
|
|
|
|
if (reverse)
|
|
|
|
used += used_1;
|
|
|
|
else if (status) {
|
|
|
|
/*
|
|
|
|
* Not having reverse hunk is not an error, but having
|
|
|
|
* a corrupt reverse hunk is.
|
|
|
|
*/
|
|
|
|
free((void*) forward->patch);
|
|
|
|
free(forward);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
forward->next = reverse;
|
|
|
|
patch->fragments = forward;
|
|
|
|
patch->is_binary = 1;
|
|
|
|
return used;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prefix_one(struct apply_state *state, char **name)
|
|
|
|
{
|
|
|
|
char *old_name = *name;
|
|
|
|
if (!old_name)
|
|
|
|
return;
|
2017-03-21 09:28:49 +08:00
|
|
|
*name = prefix_filename(state->prefix, *name);
|
2016-04-23 02:55:46 +08:00
|
|
|
free(old_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prefix_patch(struct apply_state *state, struct patch *p)
|
|
|
|
{
|
|
|
|
if (!state->prefix || p->is_toplevel_relative)
|
|
|
|
return;
|
|
|
|
prefix_one(state, &p->new_name);
|
|
|
|
prefix_one(state, &p->old_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* include/exclude
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void add_name_limit(struct apply_state *state,
|
|
|
|
const char *name,
|
|
|
|
int exclude)
|
|
|
|
{
|
|
|
|
struct string_list_item *it;
|
|
|
|
|
|
|
|
it = string_list_append(&state->limit_by_name, name);
|
|
|
|
it->util = exclude ? NULL : (void *) 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int use_patch(struct apply_state *state, struct patch *p)
|
|
|
|
{
|
|
|
|
const char *pathname = p->new_name ? p->new_name : p->old_name;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Paths outside are not touched regardless of "--include" */
|
2017-08-09 23:54:46 +08:00
|
|
|
if (state->prefix && *state->prefix) {
|
|
|
|
const char *rest;
|
|
|
|
if (!skip_prefix(pathname, state->prefix, &rest) || !*rest)
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* See if it matches any of exclude/include rule */
|
|
|
|
for (i = 0; i < state->limit_by_name.nr; i++) {
|
|
|
|
struct string_list_item *it = &state->limit_by_name.items[i];
|
2017-06-23 05:38:08 +08:00
|
|
|
if (!wildmatch(it->string, pathname, 0))
|
2016-04-23 02:55:46 +08:00
|
|
|
return (it->util != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we had any include, a path that does not match any rule is
|
|
|
|
* not used. Otherwise, we saw bunch of exclude rules (or none)
|
|
|
|
* and such a path is used.
|
|
|
|
*/
|
|
|
|
return !state->has_include;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the patch text in "buffer" that extends for "size" bytes; stop
|
|
|
|
* reading after seeing a single patch (i.e. changes to a single file).
|
|
|
|
* Create fragments (i.e. patch hunks) and hang them to the given patch.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* -1 if no header was found or parse_binary() failed,
|
|
|
|
* -128 on another error,
|
|
|
|
* the number of bytes consumed otherwise,
|
|
|
|
* so that the caller can call us again for the next patch.
|
|
|
|
*/
|
|
|
|
static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch)
|
|
|
|
{
|
|
|
|
int hdrsize, patchsize;
|
|
|
|
int offset = find_header(state, buffer, size, &hdrsize, patch);
|
|
|
|
|
|
|
|
if (offset < 0)
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
prefix_patch(state, patch);
|
|
|
|
|
|
|
|
if (!use_patch(state, patch))
|
|
|
|
patch->ws_rule = 0;
|
2018-09-21 23:57:37 +08:00
|
|
|
else if (patch->new_name)
|
|
|
|
patch->ws_rule = whitespace_rule(state->repo->index,
|
|
|
|
patch->new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
else
|
2018-09-21 23:57:37 +08:00
|
|
|
patch->ws_rule = whitespace_rule(state->repo->index,
|
|
|
|
patch->old_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
patchsize = parse_single_patch(state,
|
|
|
|
buffer + offset + hdrsize,
|
|
|
|
size - offset - hdrsize,
|
|
|
|
patch);
|
|
|
|
|
|
|
|
if (patchsize < 0)
|
|
|
|
return -128;
|
|
|
|
|
|
|
|
if (!patchsize) {
|
|
|
|
static const char git_binary[] = "GIT binary patch\n";
|
|
|
|
int hd = hdrsize + offset;
|
|
|
|
unsigned long llen = linelen(buffer + hd, size - hd);
|
|
|
|
|
|
|
|
if (llen == sizeof(git_binary) - 1 &&
|
|
|
|
!memcmp(git_binary, buffer + hd, llen)) {
|
|
|
|
int used;
|
|
|
|
state->linenr++;
|
|
|
|
used = parse_binary(state, buffer + hd + llen,
|
|
|
|
size - hd - llen, patch);
|
|
|
|
if (used < 0)
|
|
|
|
return -1;
|
|
|
|
if (used)
|
|
|
|
patchsize = used + llen;
|
|
|
|
else
|
|
|
|
patchsize = 0;
|
|
|
|
}
|
|
|
|
else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
|
|
|
|
static const char *binhdr[] = {
|
|
|
|
"Binary files ",
|
|
|
|
"Files ",
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
for (i = 0; binhdr[i]; i++) {
|
|
|
|
int len = strlen(binhdr[i]);
|
|
|
|
if (len < size - hd &&
|
|
|
|
!memcmp(binhdr[i], buffer + hd, len)) {
|
|
|
|
state->linenr++;
|
|
|
|
patch->is_binary = 1;
|
|
|
|
patchsize = llen;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Empty patch cannot be applied if it is a text patch
|
|
|
|
* without metadata change. A binary patch appears
|
|
|
|
* empty to us here.
|
|
|
|
*/
|
|
|
|
if ((state->apply || state->check) &&
|
|
|
|
(!patch->is_binary && !metadata_changes(patch))) {
|
|
|
|
error(_("patch with only garbage at line %d"), state->linenr);
|
|
|
|
return -128;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset + hdrsize + patchsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void reverse_patches(struct patch *p)
|
|
|
|
{
|
|
|
|
for (; p; p = p->next) {
|
|
|
|
struct fragment *frag = p->fragments;
|
|
|
|
|
2017-01-29 05:40:06 +08:00
|
|
|
SWAP(p->new_name, p->old_name);
|
2023-12-27 07:32:17 +08:00
|
|
|
if (p->new_mode)
|
|
|
|
SWAP(p->new_mode, p->old_mode);
|
2017-01-29 05:40:06 +08:00
|
|
|
SWAP(p->is_new, p->is_delete);
|
|
|
|
SWAP(p->lines_added, p->lines_deleted);
|
2018-10-15 08:02:00 +08:00
|
|
|
SWAP(p->old_oid_prefix, p->new_oid_prefix);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
for (; frag; frag = frag->next) {
|
2017-01-29 05:40:06 +08:00
|
|
|
SWAP(frag->newpos, frag->oldpos);
|
|
|
|
SWAP(frag->newlines, frag->oldlines);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char pluses[] =
|
|
|
|
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
|
|
|
|
static const char minuses[]=
|
|
|
|
"----------------------------------------------------------------------";
|
|
|
|
|
|
|
|
static void show_stats(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
struct strbuf qname = STRBUF_INIT;
|
|
|
|
char *cp = patch->new_name ? patch->new_name : patch->old_name;
|
|
|
|
int max, add, del;
|
|
|
|
|
|
|
|
quote_c_style(cp, &qname, NULL, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "scale" the filename
|
|
|
|
*/
|
|
|
|
max = state->max_len;
|
|
|
|
if (max > 50)
|
|
|
|
max = 50;
|
|
|
|
|
|
|
|
if (qname.len > max) {
|
|
|
|
cp = strchr(qname.buf + qname.len + 3 - max, '/');
|
|
|
|
if (!cp)
|
|
|
|
cp = qname.buf + qname.len + 3 - max;
|
|
|
|
strbuf_splice(&qname, 0, cp - qname.buf, "...", 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (patch->is_binary) {
|
|
|
|
printf(" %-*s | Bin\n", max, qname.buf);
|
|
|
|
strbuf_release(&qname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(" %-*s |", max, qname.buf);
|
|
|
|
strbuf_release(&qname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* scale the add/delete
|
|
|
|
*/
|
|
|
|
max = max + state->max_change > 70 ? 70 - max : state->max_change;
|
|
|
|
add = patch->lines_added;
|
|
|
|
del = patch->lines_deleted;
|
|
|
|
|
|
|
|
if (state->max_change > 0) {
|
|
|
|
int total = ((add + del) * max + state->max_change / 2) / state->max_change;
|
|
|
|
add = (add * max + state->max_change / 2) / state->max_change;
|
|
|
|
del = total - add;
|
|
|
|
}
|
|
|
|
printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
|
|
|
|
add, pluses, del, minuses);
|
|
|
|
}
|
|
|
|
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
static int read_old_data(struct stat *st, struct patch *patch,
|
|
|
|
const char *path, struct strbuf *buf)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
2018-01-14 06:49:31 +08:00
|
|
|
int conv_flags = patch->crlf_in_old ?
|
|
|
|
CONV_EOL_KEEP_CRLF : CONV_EOL_RENORMALIZE;
|
2016-04-23 02:55:46 +08:00
|
|
|
switch (st->st_mode & S_IFMT) {
|
|
|
|
case S_IFLNK:
|
|
|
|
if (strbuf_readlink(buf, path, st->st_size) < 0)
|
|
|
|
return error(_("unable to read symlink %s"), path);
|
|
|
|
return 0;
|
|
|
|
case S_IFREG:
|
|
|
|
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
|
|
|
|
return error(_("unable to open or read %s"), path);
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
/*
|
|
|
|
* "git apply" without "--index/--cached" should never look
|
|
|
|
* at the index; the target file may not have been added to
|
|
|
|
* the index yet, and we may not even be in any Git repository.
|
|
|
|
* Pass NULL to convert_to_git() to stress this; the function
|
|
|
|
* should never look at the index when explicit crlf option
|
|
|
|
* is given.
|
|
|
|
*/
|
2018-01-14 06:49:31 +08:00
|
|
|
convert_to_git(NULL, path, buf->buf, buf->len, buf, conv_flags);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the preimage, and the common lines in postimage,
|
2024-09-17 18:08:08 +08:00
|
|
|
* from buffer buf of length len.
|
2016-04-23 02:55:46 +08:00
|
|
|
*/
|
|
|
|
static void update_pre_post_images(struct image *preimage,
|
|
|
|
struct image *postimage,
|
2024-09-17 18:08:08 +08:00
|
|
|
char *buf, size_t len)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
2024-09-17 18:08:01 +08:00
|
|
|
struct image fixed_preimage = IMAGE_INIT;
|
2024-09-17 18:08:08 +08:00
|
|
|
size_t insert_pos = 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
int i, ctx, reduced;
|
2024-09-17 18:08:08 +08:00
|
|
|
const char *fixed;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the preimage with whitespace fixes. Note that we
|
|
|
|
* are not losing preimage->buf -- apply_one_fragment() will
|
|
|
|
* free "oldlines".
|
|
|
|
*/
|
2024-09-17 18:07:55 +08:00
|
|
|
image_prepare(&fixed_preimage, buf, len, 1);
|
2024-09-17 18:08:06 +08:00
|
|
|
for (i = 0; i < fixed_preimage.line_nr; i++)
|
2016-04-23 02:55:46 +08:00
|
|
|
fixed_preimage.line[i].flag = preimage->line[i].flag;
|
2024-09-17 18:08:08 +08:00
|
|
|
image_clear(preimage);
|
2016-04-23 02:55:46 +08:00
|
|
|
*preimage = fixed_preimage;
|
2024-09-17 18:08:08 +08:00
|
|
|
fixed = preimage->buf.buf;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/*
|
2024-09-17 18:08:08 +08:00
|
|
|
* Adjust the common context lines in postimage.
|
2016-04-23 02:55:46 +08:00
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
for (i = reduced = ctx = 0; i < postimage->line_nr; i++) {
|
2016-04-23 02:55:46 +08:00
|
|
|
size_t l_len = postimage->line[i].len;
|
2024-09-17 18:08:08 +08:00
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!(postimage->line[i].flag & LINE_COMMON)) {
|
|
|
|
/* an added line -- no counterparts in preimage */
|
2024-09-17 18:08:08 +08:00
|
|
|
insert_pos += l_len;
|
2016-04-23 02:55:46 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* and find the corresponding one in the fixed preimage */
|
2024-09-17 18:08:06 +08:00
|
|
|
while (ctx < preimage->line_nr &&
|
2016-04-23 02:55:46 +08:00
|
|
|
!(preimage->line[ctx].flag & LINE_COMMON)) {
|
|
|
|
fixed += preimage->line[ctx].len;
|
|
|
|
ctx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* preimage is expected to run out, if the caller
|
|
|
|
* fixed addition of trailing blank lines.
|
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
if (preimage->line_nr <= ctx) {
|
2016-04-23 02:55:46 +08:00
|
|
|
reduced++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* and copy it in, while fixing the line length */
|
|
|
|
l_len = preimage->line[ctx].len;
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_splice(&postimage->buf, insert_pos, postimage->line[i].len,
|
|
|
|
fixed, l_len);
|
|
|
|
insert_pos += l_len;
|
2016-04-23 02:55:46 +08:00
|
|
|
fixed += l_len;
|
|
|
|
postimage->line[i].len = l_len;
|
|
|
|
ctx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fix the length of the whole thing */
|
2024-09-17 18:08:06 +08:00
|
|
|
postimage->line_nr -= reduced;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2024-09-17 18:07:52 +08:00
|
|
|
/*
|
|
|
|
* Compare lines s1 of length n1 and s2 of length n2, ignoring
|
|
|
|
* whitespace difference. Returns 1 if they match, 0 otherwise
|
|
|
|
*/
|
|
|
|
static int fuzzy_matchlines(const char *s1, size_t n1,
|
|
|
|
const char *s2, size_t n2)
|
|
|
|
{
|
|
|
|
const char *end1 = s1 + n1;
|
|
|
|
const char *end2 = s2 + n2;
|
|
|
|
|
|
|
|
/* ignore line endings */
|
|
|
|
while (s1 < end1 && (end1[-1] == '\r' || end1[-1] == '\n'))
|
|
|
|
end1--;
|
|
|
|
while (s2 < end2 && (end2[-1] == '\r' || end2[-1] == '\n'))
|
|
|
|
end2--;
|
|
|
|
|
|
|
|
while (s1 < end1 && s2 < end2) {
|
|
|
|
if (isspace(*s1)) {
|
|
|
|
/*
|
|
|
|
* Skip whitespace. We check on both buffers
|
|
|
|
* because we don't want "a b" to match "ab".
|
|
|
|
*/
|
|
|
|
if (!isspace(*s2))
|
|
|
|
return 0;
|
|
|
|
while (s1 < end1 && isspace(*s1))
|
|
|
|
s1++;
|
|
|
|
while (s2 < end2 && isspace(*s2))
|
|
|
|
s2++;
|
|
|
|
} else if (*s1++ != *s2++)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we reached the end on one side only, lines don't match. */
|
|
|
|
return s1 == end1 && s2 == end2;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int line_by_line_fuzzy_match(struct image *img,
|
|
|
|
struct image *preimage,
|
|
|
|
struct image *postimage,
|
2018-02-15 02:59:29 +08:00
|
|
|
unsigned long current,
|
|
|
|
int current_lno,
|
2016-04-23 02:55:46 +08:00
|
|
|
int preimage_limit)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
size_t imgoff = 0;
|
|
|
|
size_t preoff = 0;
|
|
|
|
size_t extra_chars;
|
|
|
|
char *buf;
|
|
|
|
char *preimage_eof;
|
|
|
|
char *preimage_end;
|
|
|
|
struct strbuf fixed;
|
|
|
|
char *fixed_buf;
|
|
|
|
size_t fixed_len;
|
|
|
|
|
|
|
|
for (i = 0; i < preimage_limit; i++) {
|
|
|
|
size_t prelen = preimage->line[i].len;
|
2018-02-15 02:59:29 +08:00
|
|
|
size_t imglen = img->line[current_lno+i].len;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-09-17 18:08:08 +08:00
|
|
|
if (!fuzzy_matchlines(img->buf.buf + current + imgoff, imglen,
|
|
|
|
preimage->buf.buf + preoff, prelen))
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
imgoff += imglen;
|
|
|
|
preoff += prelen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, the preimage matches with whitespace fuzz.
|
|
|
|
*
|
|
|
|
* imgoff now holds the true length of the target that
|
|
|
|
* matches the preimage before the end of the file.
|
|
|
|
*
|
|
|
|
* Count the number of characters in the preimage that fall
|
|
|
|
* beyond the end of the file and make sure that all of them
|
|
|
|
* are whitespace characters. (This can only happen if
|
|
|
|
* we are removing blank lines at the end of the file.)
|
|
|
|
*/
|
2024-09-17 18:08:08 +08:00
|
|
|
buf = preimage_eof = preimage->buf.buf + preoff;
|
2024-09-17 18:08:06 +08:00
|
|
|
for ( ; i < preimage->line_nr; i++)
|
2016-04-23 02:55:46 +08:00
|
|
|
preoff += preimage->line[i].len;
|
2024-09-17 18:08:08 +08:00
|
|
|
preimage_end = preimage->buf.buf + preoff;
|
2016-04-23 02:55:46 +08:00
|
|
|
for ( ; buf < preimage_end; buf++)
|
|
|
|
if (!isspace(*buf))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the preimage and the common postimage context
|
|
|
|
* lines to use the same whitespace as the target.
|
|
|
|
* If whitespace is missing in the target (i.e.
|
|
|
|
* if the preimage extends beyond the end of the file),
|
|
|
|
* use the whitespace from the preimage.
|
|
|
|
*/
|
|
|
|
extra_chars = preimage_end - preimage_eof;
|
|
|
|
strbuf_init(&fixed, imgoff + extra_chars);
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_add(&fixed, img->buf.buf + current, imgoff);
|
2016-04-23 02:55:46 +08:00
|
|
|
strbuf_add(&fixed, preimage_eof, extra_chars);
|
|
|
|
fixed_buf = strbuf_detach(&fixed, &fixed_len);
|
|
|
|
update_pre_post_images(preimage, postimage,
|
2024-09-17 18:08:08 +08:00
|
|
|
fixed_buf, fixed_len);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match_fragment(struct apply_state *state,
|
|
|
|
struct image *img,
|
|
|
|
struct image *preimage,
|
|
|
|
struct image *postimage,
|
2018-02-15 02:59:29 +08:00
|
|
|
unsigned long current,
|
|
|
|
int current_lno,
|
2016-04-23 02:55:46 +08:00
|
|
|
unsigned ws_rule,
|
|
|
|
int match_beginning, int match_end)
|
|
|
|
{
|
|
|
|
int i;
|
2024-06-11 17:20:52 +08:00
|
|
|
const char *orig, *target;
|
|
|
|
struct strbuf fixed = STRBUF_INIT;
|
2024-09-17 18:08:08 +08:00
|
|
|
char *fixed_buf;
|
|
|
|
size_t fixed_len;
|
2016-04-23 02:55:46 +08:00
|
|
|
int preimage_limit;
|
2024-06-11 17:20:52 +08:00
|
|
|
int ret;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-09-17 18:08:06 +08:00
|
|
|
if (preimage->line_nr + current_lno <= img->line_nr) {
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* The hunk falls within the boundaries of img.
|
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
preimage_limit = preimage->line_nr;
|
|
|
|
if (match_end && (preimage->line_nr + current_lno != img->line_nr)) {
|
2024-06-11 17:20:52 +08:00
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
} else if (state->ws_error_action == correct_ws_error &&
|
|
|
|
(ws_rule & WS_BLANK_AT_EOF)) {
|
|
|
|
/*
|
|
|
|
* This hunk extends beyond the end of img, and we are
|
|
|
|
* removing blank lines at the end of the file. This
|
|
|
|
* many lines from the beginning of the preimage must
|
|
|
|
* match with img, and the remainder of the preimage
|
|
|
|
* must be blank.
|
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
preimage_limit = img->line_nr - current_lno;
|
2016-04-23 02:55:46 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The hunk extends beyond the end of the img and
|
|
|
|
* we are not removing blanks at the end, so we
|
|
|
|
* should reject the hunk at this position.
|
|
|
|
*/
|
2024-06-11 17:20:52 +08:00
|
|
|
ret = 0;
|
|
|
|
goto out;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2024-06-11 17:20:52 +08:00
|
|
|
if (match_beginning && current_lno) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* Quick hash check */
|
2024-06-11 17:20:52 +08:00
|
|
|
for (i = 0; i < preimage_limit; i++) {
|
2018-02-15 02:59:29 +08:00
|
|
|
if ((img->line[current_lno + i].flag & LINE_PATCHED) ||
|
2024-06-11 17:20:52 +08:00
|
|
|
(preimage->line[i].hash != img->line[current_lno + i].hash)) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-09-17 18:08:06 +08:00
|
|
|
if (preimage_limit == preimage->line_nr) {
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* Do we have an exact match? If we were told to match
|
2018-02-15 02:59:29 +08:00
|
|
|
* at the end, size must be exactly at current+fragsize,
|
|
|
|
* otherwise current+fragsize must be still within the preimage,
|
2016-04-23 02:55:46 +08:00
|
|
|
* and either case, the old piece should match the preimage
|
|
|
|
* exactly.
|
|
|
|
*/
|
|
|
|
if ((match_end
|
2024-09-17 18:08:08 +08:00
|
|
|
? (current + preimage->buf.len == img->buf.len)
|
|
|
|
: (current + preimage->buf.len <= img->buf.len)) &&
|
|
|
|
!memcmp(img->buf.buf + current, preimage->buf.buf, preimage->buf.len)) {
|
2024-06-11 17:20:52 +08:00
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The preimage extends beyond the end of img, so
|
|
|
|
* there cannot be an exact match.
|
|
|
|
*
|
|
|
|
* There must be one non-blank context line that match
|
|
|
|
* a line before the end of img.
|
|
|
|
*/
|
2024-06-11 17:20:52 +08:00
|
|
|
const char *buf, *buf_end;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-09-17 18:08:08 +08:00
|
|
|
buf = preimage->buf.buf;
|
2016-04-23 02:55:46 +08:00
|
|
|
buf_end = buf;
|
|
|
|
for (i = 0; i < preimage_limit; i++)
|
|
|
|
buf_end += preimage->line[i].len;
|
|
|
|
|
|
|
|
for ( ; buf < buf_end; buf++)
|
|
|
|
if (!isspace(*buf))
|
|
|
|
break;
|
2024-06-11 17:20:52 +08:00
|
|
|
if (buf == buf_end) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No exact match. If we are ignoring whitespace, run a line-by-line
|
|
|
|
* fuzzy matching. We collect all the line length information because
|
|
|
|
* we need it to adjust whitespace if we match.
|
|
|
|
*/
|
2024-06-11 17:20:52 +08:00
|
|
|
if (state->ws_ignore_action == ignore_ws_change) {
|
|
|
|
ret = line_by_line_fuzzy_match(img, preimage, postimage,
|
|
|
|
current, current_lno, preimage_limit);
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-06-11 17:20:52 +08:00
|
|
|
if (state->ws_error_action != correct_ws_error) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The hunk does not apply byte-by-byte, but the hash says
|
|
|
|
* it might with whitespace fuzz. We weren't asked to
|
|
|
|
* ignore whitespace, we were asked to correct whitespace
|
|
|
|
* errors, so let's try matching after whitespace correction.
|
|
|
|
*
|
|
|
|
* While checking the preimage against the target, whitespace
|
|
|
|
* errors in both fixed, we count how large the corresponding
|
|
|
|
* postimage needs to be. The postimage prepared by
|
|
|
|
* apply_one_fragment() has whitespace errors fixed on added
|
|
|
|
* lines already, but the common lines were propagated as-is,
|
|
|
|
* which may become longer when their whitespace errors are
|
|
|
|
* fixed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The preimage may extend beyond the end of the file,
|
|
|
|
* but in this loop we will only handle the part of the
|
|
|
|
* preimage that falls within the file.
|
|
|
|
*/
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_grow(&fixed, preimage->buf.len + 1);
|
|
|
|
orig = preimage->buf.buf;
|
|
|
|
target = img->buf.buf + current;
|
2016-04-23 02:55:46 +08:00
|
|
|
for (i = 0; i < preimage_limit; i++) {
|
|
|
|
size_t oldlen = preimage->line[i].len;
|
2018-02-15 02:59:29 +08:00
|
|
|
size_t tgtlen = img->line[current_lno + i].len;
|
2016-04-23 02:55:46 +08:00
|
|
|
size_t fixstart = fixed.len;
|
|
|
|
struct strbuf tgtfix;
|
|
|
|
int match;
|
|
|
|
|
|
|
|
/* Try fixing the line in the preimage */
|
|
|
|
ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
|
|
|
|
|
|
|
|
/* Try fixing the line in the target */
|
|
|
|
strbuf_init(&tgtfix, tgtlen);
|
|
|
|
ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If they match, either the preimage was based on
|
|
|
|
* a version before our tree fixed whitespace breakage,
|
|
|
|
* or we are lacking a whitespace-fix patch the tree
|
|
|
|
* the preimage was based on already had (i.e. target
|
|
|
|
* has whitespace breakage, the preimage doesn't).
|
|
|
|
* In either case, we are fixing the whitespace breakages
|
|
|
|
* so we might as well take the fix together with their
|
|
|
|
* real change.
|
|
|
|
*/
|
|
|
|
match = (tgtfix.len == fixed.len - fixstart &&
|
|
|
|
!memcmp(tgtfix.buf, fixed.buf + fixstart,
|
|
|
|
fixed.len - fixstart));
|
|
|
|
|
|
|
|
strbuf_release(&tgtfix);
|
2024-06-11 17:20:52 +08:00
|
|
|
if (!match) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
orig += oldlen;
|
|
|
|
target += tgtlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now handle the lines in the preimage that falls beyond the
|
|
|
|
* end of the file (if any). They will only match if they are
|
|
|
|
* empty or only contain whitespace (if WS_BLANK_AT_EOL is
|
|
|
|
* false).
|
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
for ( ; i < preimage->line_nr; i++) {
|
2016-04-23 02:55:46 +08:00
|
|
|
size_t fixstart = fixed.len; /* start of the fixed preimage */
|
|
|
|
size_t oldlen = preimage->line[i].len;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
/* Try fixing the line in the preimage */
|
|
|
|
ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
|
|
|
|
|
2024-06-11 17:20:52 +08:00
|
|
|
for (j = fixstart; j < fixed.len; j++) {
|
|
|
|
if (!isspace(fixed.buf[j])) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
orig += oldlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Yes, the preimage is based on an older version that still
|
|
|
|
* has whitespace breakages unfixed, and fixing them makes the
|
|
|
|
* hunk match. Update the context lines in the postimage.
|
|
|
|
*/
|
2024-09-17 18:08:08 +08:00
|
|
|
fixed_buf = strbuf_detach(&fixed, &fixed_len);
|
2016-04-23 02:55:46 +08:00
|
|
|
update_pre_post_images(preimage, postimage,
|
2024-09-17 18:08:08 +08:00
|
|
|
fixed_buf, fixed_len);
|
2024-06-11 17:20:52 +08:00
|
|
|
|
|
|
|
ret = 1;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2024-06-11 17:20:52 +08:00
|
|
|
out:
|
2016-04-23 02:55:46 +08:00
|
|
|
strbuf_release(&fixed);
|
2024-06-11 17:20:52 +08:00
|
|
|
return ret;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int find_pos(struct apply_state *state,
|
|
|
|
struct image *img,
|
|
|
|
struct image *preimage,
|
|
|
|
struct image *postimage,
|
|
|
|
int line,
|
|
|
|
unsigned ws_rule,
|
|
|
|
int match_beginning, int match_end)
|
|
|
|
{
|
|
|
|
int i;
|
2018-02-15 02:59:29 +08:00
|
|
|
unsigned long backwards, forwards, current;
|
|
|
|
int backwards_lno, forwards_lno, current_lno;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2019-12-06 21:08:25 +08:00
|
|
|
/*
|
|
|
|
* When running with --allow-overlap, it is possible that a hunk is
|
|
|
|
* seen that pretends to start at the beginning (but no longer does),
|
|
|
|
* and that *still* needs to match the end. So trust `match_end` more
|
|
|
|
* than `match_beginning`.
|
|
|
|
*/
|
|
|
|
if (state->allow_overlap && match_beginning && match_end &&
|
2024-09-17 18:08:06 +08:00
|
|
|
img->line_nr - preimage->line_nr != 0)
|
2019-12-06 21:08:25 +08:00
|
|
|
match_beginning = 0;
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* If match_beginning or match_end is specified, there is no
|
|
|
|
* point starting from a wrong line that will never match and
|
|
|
|
* wander around and wait for a match at the specified end.
|
|
|
|
*/
|
|
|
|
if (match_beginning)
|
|
|
|
line = 0;
|
|
|
|
else if (match_end)
|
2024-09-17 18:08:06 +08:00
|
|
|
line = img->line_nr - preimage->line_nr;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Because the comparison is unsigned, the following test
|
|
|
|
* will also take care of a negative line number that can
|
|
|
|
* result when match_end and preimage is larger than the target.
|
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
if ((size_t) line > img->line_nr)
|
|
|
|
line = img->line_nr;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2018-02-15 02:59:29 +08:00
|
|
|
current = 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
for (i = 0; i < line; i++)
|
2018-02-15 02:59:29 +08:00
|
|
|
current += img->line[i].len;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* There's probably some smart way to do this, but I'll leave
|
|
|
|
* that to the smart and beautiful people. I'm simple and stupid.
|
|
|
|
*/
|
2018-02-15 02:59:29 +08:00
|
|
|
backwards = current;
|
2016-04-23 02:55:46 +08:00
|
|
|
backwards_lno = line;
|
2018-02-15 02:59:29 +08:00
|
|
|
forwards = current;
|
2016-04-23 02:55:46 +08:00
|
|
|
forwards_lno = line;
|
2018-02-15 02:59:29 +08:00
|
|
|
current_lno = line;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
if (match_fragment(state, img, preimage, postimage,
|
2018-02-15 02:59:29 +08:00
|
|
|
current, current_lno, ws_rule,
|
2016-04-23 02:55:46 +08:00
|
|
|
match_beginning, match_end))
|
2018-02-15 02:59:29 +08:00
|
|
|
return current_lno;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
again:
|
2024-09-17 18:08:06 +08:00
|
|
|
if (backwards_lno == 0 && forwards_lno == img->line_nr)
|
2016-04-23 02:55:46 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (i & 1) {
|
|
|
|
if (backwards_lno == 0) {
|
|
|
|
i++;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
backwards_lno--;
|
|
|
|
backwards -= img->line[backwards_lno].len;
|
2018-02-15 02:59:29 +08:00
|
|
|
current = backwards;
|
|
|
|
current_lno = backwards_lno;
|
2016-04-23 02:55:46 +08:00
|
|
|
} else {
|
2024-09-17 18:08:06 +08:00
|
|
|
if (forwards_lno == img->line_nr) {
|
2016-04-23 02:55:46 +08:00
|
|
|
i++;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
forwards += img->line[forwards_lno].len;
|
|
|
|
forwards_lno++;
|
2018-02-15 02:59:29 +08:00
|
|
|
current = forwards;
|
|
|
|
current_lno = forwards_lno;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The change from "preimage" and "postimage" has been found to
|
|
|
|
* apply at applied_pos (counts in line numbers) in "img".
|
|
|
|
* Update "img" to remove "preimage" and replace it with "postimage".
|
|
|
|
*/
|
|
|
|
static void update_image(struct apply_state *state,
|
|
|
|
struct image *img,
|
|
|
|
int applied_pos,
|
|
|
|
struct image *preimage,
|
|
|
|
struct image *postimage)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* remove the copy of preimage at offset in img
|
|
|
|
* and replace it with postimage
|
|
|
|
*/
|
|
|
|
int i, nr;
|
|
|
|
size_t remove_count, insert_count, applied_at = 0;
|
2024-09-17 18:08:08 +08:00
|
|
|
size_t result_alloc;
|
2016-04-23 02:55:46 +08:00
|
|
|
char *result;
|
|
|
|
int preimage_limit;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are removing blank lines at the end of img,
|
|
|
|
* the preimage may extend beyond the end.
|
|
|
|
* If that is the case, we must be careful only to
|
|
|
|
* remove the part of the preimage that falls within
|
|
|
|
* the boundaries of img. Initialize preimage_limit
|
|
|
|
* to the number of lines in the preimage that falls
|
|
|
|
* within the boundaries.
|
|
|
|
*/
|
2024-09-17 18:08:06 +08:00
|
|
|
preimage_limit = preimage->line_nr;
|
|
|
|
if (preimage_limit > img->line_nr - applied_pos)
|
|
|
|
preimage_limit = img->line_nr - applied_pos;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
for (i = 0; i < applied_pos; i++)
|
|
|
|
applied_at += img->line[i].len;
|
|
|
|
|
|
|
|
remove_count = 0;
|
|
|
|
for (i = 0; i < preimage_limit; i++)
|
|
|
|
remove_count += img->line[applied_pos + i].len;
|
2024-09-17 18:08:08 +08:00
|
|
|
insert_count = postimage->buf.len;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* Adjust the contents */
|
2024-09-17 18:08:08 +08:00
|
|
|
result_alloc = st_add3(st_sub(img->buf.len, remove_count), insert_count, 1);
|
|
|
|
result = xmalloc(result_alloc);
|
|
|
|
memcpy(result, img->buf.buf, applied_at);
|
|
|
|
memcpy(result + applied_at, postimage->buf.buf, postimage->buf.len);
|
|
|
|
memcpy(result + applied_at + postimage->buf.len,
|
|
|
|
img->buf.buf + (applied_at + remove_count),
|
|
|
|
img->buf.len - (applied_at + remove_count));
|
|
|
|
strbuf_attach(&img->buf, result, postimage->buf.len + img->buf.len - remove_count,
|
|
|
|
result_alloc);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* Adjust the line table */
|
2024-09-17 18:08:06 +08:00
|
|
|
nr = img->line_nr + postimage->line_nr - preimage_limit;
|
|
|
|
if (preimage_limit < postimage->line_nr)
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
2024-09-17 18:07:55 +08:00
|
|
|
* NOTE: this knows that we never call image_remove_first_line()
|
2016-04-23 02:55:46 +08:00
|
|
|
* on anything other than pre/post image.
|
|
|
|
*/
|
|
|
|
REALLOC_ARRAY(img->line, nr);
|
2024-09-17 18:08:06 +08:00
|
|
|
if (preimage_limit != postimage->line_nr)
|
|
|
|
MOVE_ARRAY(img->line + applied_pos + postimage->line_nr,
|
2017-07-16 04:20:54 +08:00
|
|
|
img->line + applied_pos + preimage_limit,
|
2024-09-17 18:08:06 +08:00
|
|
|
img->line_nr - (applied_pos + preimage_limit));
|
|
|
|
COPY_ARRAY(img->line + applied_pos, postimage->line, postimage->line_nr);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!state->allow_overlap)
|
2024-09-17 18:08:06 +08:00
|
|
|
for (i = 0; i < postimage->line_nr; i++)
|
2016-04-23 02:55:46 +08:00
|
|
|
img->line[applied_pos + i].flag |= LINE_PATCHED;
|
2024-09-17 18:08:06 +08:00
|
|
|
img->line_nr = nr;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use the patch-hunk text in "frag" to prepare two images (preimage and
|
|
|
|
* postimage) for the hunk. Find lines that match "preimage" in "img" and
|
|
|
|
* replace the part of "img" with "postimage" text.
|
|
|
|
*/
|
|
|
|
static int apply_one_fragment(struct apply_state *state,
|
|
|
|
struct image *img, struct fragment *frag,
|
|
|
|
int inaccurate_eof, unsigned ws_rule,
|
|
|
|
int nth_fragment)
|
|
|
|
{
|
|
|
|
int match_beginning, match_end;
|
|
|
|
const char *patch = frag->patch;
|
|
|
|
int size = frag->size;
|
|
|
|
char *old, *oldlines;
|
|
|
|
struct strbuf newlines;
|
|
|
|
int new_blank_lines_at_end = 0;
|
|
|
|
int found_new_blank_lines_at_end = 0;
|
|
|
|
int hunk_linenr = frag->linenr;
|
|
|
|
unsigned long leading, trailing;
|
|
|
|
int pos, applied_pos;
|
2024-09-17 18:08:01 +08:00
|
|
|
struct image preimage = IMAGE_INIT;
|
|
|
|
struct image postimage = IMAGE_INIT;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
oldlines = xmalloc(size);
|
|
|
|
strbuf_init(&newlines, size);
|
|
|
|
|
|
|
|
old = oldlines;
|
|
|
|
while (size > 0) {
|
|
|
|
char first;
|
|
|
|
int len = linelen(patch, size);
|
|
|
|
int plen;
|
|
|
|
int added_blank_line = 0;
|
|
|
|
int is_blank_context = 0;
|
|
|
|
size_t start;
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "plen" is how much of the line we should use for
|
|
|
|
* the actual patch data. Normally we just remove the
|
|
|
|
* first character on the line, but if the line is
|
|
|
|
* followed by "\ No newline", then we also remove the
|
|
|
|
* last one (which is the newline, of course).
|
|
|
|
*/
|
|
|
|
plen = len - 1;
|
|
|
|
if (len < size && patch[len] == '\\')
|
|
|
|
plen--;
|
|
|
|
first = *patch;
|
|
|
|
if (state->apply_in_reverse) {
|
|
|
|
if (first == '-')
|
|
|
|
first = '+';
|
|
|
|
else if (first == '+')
|
|
|
|
first = '-';
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (first) {
|
|
|
|
case '\n':
|
|
|
|
/* Newer GNU diff, empty context line */
|
|
|
|
if (plen < 0)
|
|
|
|
/* ... followed by '\No newline'; nothing */
|
|
|
|
break;
|
|
|
|
*old++ = '\n';
|
|
|
|
strbuf_addch(&newlines, '\n');
|
2024-09-17 18:07:55 +08:00
|
|
|
image_add_line(&preimage, "\n", 1, LINE_COMMON);
|
|
|
|
image_add_line(&postimage, "\n", 1, LINE_COMMON);
|
2016-04-23 02:55:46 +08:00
|
|
|
is_blank_context = 1;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
|
2022-12-13 19:12:58 +08:00
|
|
|
ws_blank_line(patch + 1, plen))
|
2016-04-23 02:55:46 +08:00
|
|
|
is_blank_context = 1;
|
consistently use "fallthrough" comments in switches
Gcc 7 adds -Wimplicit-fallthrough, which can warn when a
switch case falls through to the next case. The general idea
is that the compiler can't tell if this was intentional or
not, so you should annotate any intentional fall-throughs as
such, leaving it to complain about any unannotated ones.
There's a GNU __attribute__ which can be used for
annotation, but of course we'd have to #ifdef it away on
non-gcc compilers. Gcc will also recognize
specially-formatted comments, which matches our current
practice. Let's extend that practice to all of the
unannotated sites (which I did look over and verify that
they were behaving as intended).
Ideally in each case we'd actually give some reasons in the
comment about why we're falling through, or what we're
falling through to. And gcc does support that with
-Wimplicit-fallthrough=2, which relaxes the comment pattern
matching to anything that contains "fallthrough" (or a
variety of spelling variants). However, this isn't the
default for -Wimplicit-fallthrough, nor for -Wextra. In the
name of simplicity, it's probably better for us to support
the default level, which requires "fallthrough" to be the
only thing in the comment (modulo some window dressing like
"else" and some punctuation; see the gcc manual for the
complete set of patterns).
This patch suppresses all warnings due to
-Wimplicit-fallthrough. We might eventually want to add that
to the DEVELOPER Makefile knob, but we should probably wait
until gcc 7 is more widely adopted (since earlier versions
will complain about the unknown warning type).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-21 14:25:41 +08:00
|
|
|
/* fallthrough */
|
2016-04-23 02:55:46 +08:00
|
|
|
case '-':
|
|
|
|
memcpy(old, patch + 1, plen);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_add_line(&preimage, old, plen,
|
2016-04-23 02:55:46 +08:00
|
|
|
(first == ' ' ? LINE_COMMON : 0));
|
|
|
|
old += plen;
|
|
|
|
if (first == '-')
|
|
|
|
break;
|
consistently use "fallthrough" comments in switches
Gcc 7 adds -Wimplicit-fallthrough, which can warn when a
switch case falls through to the next case. The general idea
is that the compiler can't tell if this was intentional or
not, so you should annotate any intentional fall-throughs as
such, leaving it to complain about any unannotated ones.
There's a GNU __attribute__ which can be used for
annotation, but of course we'd have to #ifdef it away on
non-gcc compilers. Gcc will also recognize
specially-formatted comments, which matches our current
practice. Let's extend that practice to all of the
unannotated sites (which I did look over and verify that
they were behaving as intended).
Ideally in each case we'd actually give some reasons in the
comment about why we're falling through, or what we're
falling through to. And gcc does support that with
-Wimplicit-fallthrough=2, which relaxes the comment pattern
matching to anything that contains "fallthrough" (or a
variety of spelling variants). However, this isn't the
default for -Wimplicit-fallthrough, nor for -Wextra. In the
name of simplicity, it's probably better for us to support
the default level, which requires "fallthrough" to be the
only thing in the comment (modulo some window dressing like
"else" and some punctuation; see the gcc manual for the
complete set of patterns).
This patch suppresses all warnings due to
-Wimplicit-fallthrough. We might eventually want to add that
to the DEVELOPER Makefile knob, but we should probably wait
until gcc 7 is more widely adopted (since earlier versions
will complain about the unknown warning type).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-21 14:25:41 +08:00
|
|
|
/* fallthrough */
|
2016-04-23 02:55:46 +08:00
|
|
|
case '+':
|
|
|
|
/* --no-add does not add new lines */
|
|
|
|
if (first == '+' && state->no_add)
|
|
|
|
break;
|
|
|
|
|
|
|
|
start = newlines.len;
|
|
|
|
if (first != '+' ||
|
|
|
|
!state->whitespace_error ||
|
|
|
|
state->ws_error_action != correct_ws_error) {
|
|
|
|
strbuf_add(&newlines, patch + 1, plen);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws);
|
|
|
|
}
|
2024-09-17 18:07:55 +08:00
|
|
|
image_add_line(&postimage, newlines.buf + start, newlines.len - start,
|
2016-04-23 02:55:46 +08:00
|
|
|
(first == '+' ? 0 : LINE_COMMON));
|
|
|
|
if (first == '+' &&
|
|
|
|
(ws_rule & WS_BLANK_AT_EOF) &&
|
2022-12-13 19:12:58 +08:00
|
|
|
ws_blank_line(patch + 1, plen))
|
2016-04-23 02:55:46 +08:00
|
|
|
added_blank_line = 1;
|
|
|
|
break;
|
|
|
|
case '@': case '\\':
|
|
|
|
/* Ignore it, we already handled it */
|
|
|
|
break;
|
|
|
|
default:
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_normal)
|
2016-04-23 02:55:46 +08:00
|
|
|
error(_("invalid start of line: '%c'"), first);
|
|
|
|
applied_pos = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (added_blank_line) {
|
|
|
|
if (!new_blank_lines_at_end)
|
|
|
|
found_new_blank_lines_at_end = hunk_linenr;
|
|
|
|
new_blank_lines_at_end++;
|
|
|
|
}
|
|
|
|
else if (is_blank_context)
|
|
|
|
;
|
|
|
|
else
|
|
|
|
new_blank_lines_at_end = 0;
|
|
|
|
patch += len;
|
|
|
|
size -= len;
|
|
|
|
hunk_linenr++;
|
|
|
|
}
|
|
|
|
if (inaccurate_eof &&
|
|
|
|
old > oldlines && old[-1] == '\n' &&
|
|
|
|
newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
|
|
|
|
old--;
|
|
|
|
strbuf_setlen(&newlines, newlines.len - 1);
|
2024-09-17 18:08:06 +08:00
|
|
|
preimage.line[preimage.line_nr - 1].len--;
|
|
|
|
postimage.line[postimage.line_nr - 1].len--;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
leading = frag->leading;
|
|
|
|
trailing = frag->trailing;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A hunk to change lines at the beginning would begin with
|
|
|
|
* @@ -1,L +N,M @@
|
|
|
|
* but we need to be careful. -U0 that inserts before the second
|
|
|
|
* line also has this pattern.
|
|
|
|
*
|
|
|
|
* And a hunk to add to an empty file would begin with
|
|
|
|
* @@ -0,0 +N,M @@
|
|
|
|
*
|
|
|
|
* In other words, a hunk that is (frag->oldpos <= 1) with or
|
|
|
|
* without leading context must match at the beginning.
|
|
|
|
*/
|
|
|
|
match_beginning = (!frag->oldpos ||
|
|
|
|
(frag->oldpos == 1 && !state->unidiff_zero));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A hunk without trailing lines must match at the end.
|
|
|
|
* However, we simply cannot tell if a hunk must match end
|
|
|
|
* from the lack of trailing lines if the patch was generated
|
|
|
|
* with unidiff without any context.
|
|
|
|
*/
|
|
|
|
match_end = !state->unidiff_zero && !trailing;
|
|
|
|
|
|
|
|
pos = frag->newpos ? (frag->newpos - 1) : 0;
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_add(&preimage.buf, oldlines, old - oldlines);
|
|
|
|
strbuf_swap(&postimage.buf, &newlines);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
applied_pos = find_pos(state, img, &preimage, &postimage, pos,
|
|
|
|
ws_rule, match_beginning, match_end);
|
|
|
|
|
|
|
|
if (applied_pos >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Am I at my context limits? */
|
|
|
|
if ((leading <= state->p_context) && (trailing <= state->p_context))
|
|
|
|
break;
|
|
|
|
if (match_beginning || match_end) {
|
|
|
|
match_beginning = match_end = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reduce the number of context lines; reduce both
|
|
|
|
* leading and trailing if they are equal otherwise
|
|
|
|
* just reduce the larger context.
|
|
|
|
*/
|
|
|
|
if (leading >= trailing) {
|
2024-09-17 18:07:55 +08:00
|
|
|
image_remove_first_line(&preimage);
|
|
|
|
image_remove_first_line(&postimage);
|
2016-04-23 02:55:46 +08:00
|
|
|
pos--;
|
|
|
|
leading--;
|
|
|
|
}
|
|
|
|
if (trailing > leading) {
|
2024-09-17 18:07:55 +08:00
|
|
|
image_remove_last_line(&preimage);
|
|
|
|
image_remove_last_line(&postimage);
|
2016-04-23 02:55:46 +08:00
|
|
|
trailing--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (applied_pos >= 0) {
|
|
|
|
if (new_blank_lines_at_end &&
|
2024-09-17 18:08:06 +08:00
|
|
|
preimage.line_nr + applied_pos >= img->line_nr &&
|
2016-04-23 02:55:46 +08:00
|
|
|
(ws_rule & WS_BLANK_AT_EOF) &&
|
|
|
|
state->ws_error_action != nowarn_ws_error) {
|
|
|
|
record_ws_error(state, WS_BLANK_AT_EOF, "+", 1,
|
|
|
|
found_new_blank_lines_at_end);
|
|
|
|
if (state->ws_error_action == correct_ws_error) {
|
|
|
|
while (new_blank_lines_at_end--)
|
2024-09-17 18:07:55 +08:00
|
|
|
image_remove_last_line(&postimage);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We would want to prevent write_out_results()
|
|
|
|
* from taking place in apply_patch() that follows
|
|
|
|
* the callchain led us here, which is:
|
|
|
|
* apply_patch->check_patch_list->check_patch->
|
|
|
|
* apply_data->apply_fragments->apply_one_fragment
|
|
|
|
*/
|
|
|
|
if (state->ws_error_action == die_on_ws_error)
|
|
|
|
state->apply = 0;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_normal && applied_pos != pos) {
|
2016-04-23 02:55:46 +08:00
|
|
|
int offset = applied_pos - pos;
|
|
|
|
if (state->apply_in_reverse)
|
|
|
|
offset = 0 - offset;
|
|
|
|
fprintf_ln(stderr,
|
|
|
|
Q_("Hunk #%d succeeded at %d (offset %d line).",
|
|
|
|
"Hunk #%d succeeded at %d (offset %d lines).",
|
|
|
|
offset),
|
|
|
|
nth_fragment, applied_pos + 1, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Warn if it was necessary to reduce the number
|
|
|
|
* of context lines.
|
|
|
|
*/
|
2016-09-05 04:18:25 +08:00
|
|
|
if ((leading != frag->leading ||
|
|
|
|
trailing != frag->trailing) && state->apply_verbosity > verbosity_silent)
|
2016-04-23 02:55:46 +08:00
|
|
|
fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
|
|
|
|
" to apply fragment at %d"),
|
|
|
|
leading, trailing, applied_pos+1);
|
|
|
|
update_image(state, img, applied_pos, &preimage, &postimage);
|
|
|
|
} else {
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_normal)
|
2016-04-23 02:55:46 +08:00
|
|
|
error(_("while searching for:\n%.*s"),
|
|
|
|
(int)(old - oldlines), oldlines);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(oldlines);
|
|
|
|
strbuf_release(&newlines);
|
2024-09-17 18:08:08 +08:00
|
|
|
image_clear(&preimage);
|
|
|
|
image_clear(&postimage);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
return (applied_pos < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int apply_binary_fragment(struct apply_state *state,
|
|
|
|
struct image *img,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
struct fragment *fragment = patch->fragments;
|
|
|
|
unsigned long len;
|
|
|
|
void *dst;
|
|
|
|
|
|
|
|
if (!fragment)
|
|
|
|
return error(_("missing binary patch data for '%s'"),
|
|
|
|
patch->new_name ?
|
|
|
|
patch->new_name :
|
|
|
|
patch->old_name);
|
|
|
|
|
|
|
|
/* Binary patch is irreversible without the optional second hunk */
|
|
|
|
if (state->apply_in_reverse) {
|
|
|
|
if (!fragment->next)
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("cannot reverse-apply a binary patch "
|
|
|
|
"without the reverse hunk to '%s'"),
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->new_name
|
|
|
|
? patch->new_name : patch->old_name);
|
|
|
|
fragment = fragment->next;
|
|
|
|
}
|
|
|
|
switch (fragment->binary_patch_method) {
|
|
|
|
case BINARY_DELTA_DEFLATED:
|
2024-09-17 18:08:08 +08:00
|
|
|
dst = patch_delta(img->buf.buf, img->buf.len, fragment->patch,
|
2016-04-23 02:55:46 +08:00
|
|
|
fragment->size, &len);
|
|
|
|
if (!dst)
|
|
|
|
return -1;
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(img);
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_attach(&img->buf, dst, len, len + 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
case BINARY_LITERAL_DEFLATED:
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(img);
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_add(&img->buf, fragment->patch, fragment->size);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace "img" with the result of applying the binary patch.
|
|
|
|
* The binary patch data itself in patch->fragment is still kept
|
|
|
|
* but the preimage prepared by the caller in "img" is freed here
|
|
|
|
* or in the helper function apply_binary_fragment() this calls.
|
|
|
|
*/
|
|
|
|
static int apply_binary(struct apply_state *state,
|
|
|
|
struct image *img,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
2016-09-20 04:47:19 +08:00
|
|
|
struct object_id oid;
|
2018-10-15 08:01:59 +08:00
|
|
|
const unsigned hexsz = the_hash_algo->hexsz;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For safety, we require patch index line to contain
|
2018-10-15 08:01:59 +08:00
|
|
|
* full hex textual object ID for old and new, at least for now.
|
2016-04-23 02:55:46 +08:00
|
|
|
*/
|
2018-10-15 08:02:00 +08:00
|
|
|
if (strlen(patch->old_oid_prefix) != hexsz ||
|
|
|
|
strlen(patch->new_oid_prefix) != hexsz ||
|
|
|
|
get_oid_hex(patch->old_oid_prefix, &oid) ||
|
|
|
|
get_oid_hex(patch->new_oid_prefix, &oid))
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("cannot apply binary patch to '%s' "
|
|
|
|
"without full index line"), name);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
if (patch->old_name) {
|
|
|
|
/*
|
|
|
|
* See if the old one matches what the patch
|
|
|
|
* applies to.
|
|
|
|
*/
|
2024-09-17 18:08:08 +08:00
|
|
|
hash_object_file(the_hash_algo, img->buf.buf, img->buf.len,
|
|
|
|
OBJ_BLOB, &oid);
|
2018-10-15 08:02:00 +08:00
|
|
|
if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix))
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("the patch applies to '%s' (%s), "
|
|
|
|
"which does not match the "
|
|
|
|
"current contents."),
|
2016-09-20 04:47:19 +08:00
|
|
|
name, oid_to_hex(&oid));
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Otherwise, the old one must be empty. */
|
2024-09-17 18:08:08 +08:00
|
|
|
if (img->buf.len)
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("the patch applies to an empty "
|
|
|
|
"'%s' but it is not empty"), name);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2018-10-15 08:02:00 +08:00
|
|
|
get_oid_hex(patch->new_oid_prefix, &oid);
|
2016-09-20 04:47:19 +08:00
|
|
|
if (is_null_oid(&oid)) {
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(img);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0; /* deletion patch */
|
|
|
|
}
|
|
|
|
|
2020-08-06 07:06:50 +08:00
|
|
|
if (has_object(the_repository, &oid, 0)) {
|
2016-04-23 02:55:46 +08:00
|
|
|
/* We already have the postimage */
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long size;
|
|
|
|
char *result;
|
|
|
|
|
2023-03-28 21:58:50 +08:00
|
|
|
result = repo_read_object_file(the_repository, &oid, &type,
|
|
|
|
&size);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!result)
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("the necessary postimage %s for "
|
|
|
|
"'%s' cannot be read"),
|
2018-10-15 08:02:00 +08:00
|
|
|
patch->new_oid_prefix, name);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(img);
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_attach(&img->buf, result, size, size + 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We have verified buf matches the preimage;
|
|
|
|
* apply the patch data to it, which is stored
|
|
|
|
* in the patch->fragments->{patch,size}.
|
|
|
|
*/
|
|
|
|
if (apply_binary_fragment(state, img, patch))
|
|
|
|
return error(_("binary patch does not apply to '%s'"),
|
|
|
|
name);
|
|
|
|
|
|
|
|
/* verify that the result matches */
|
2024-09-17 18:08:08 +08:00
|
|
|
hash_object_file(the_hash_algo, img->buf.buf, img->buf.len, OBJ_BLOB,
|
2020-01-31 04:32:22 +08:00
|
|
|
&oid);
|
2018-10-15 08:02:00 +08:00
|
|
|
if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
|
2018-10-15 08:02:00 +08:00
|
|
|
name, patch->new_oid_prefix, oid_to_hex(&oid));
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch)
|
|
|
|
{
|
|
|
|
struct fragment *frag = patch->fragments;
|
|
|
|
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
|
|
|
unsigned ws_rule = patch->ws_rule;
|
|
|
|
unsigned inaccurate_eof = patch->inaccurate_eof;
|
|
|
|
int nth = 0;
|
|
|
|
|
|
|
|
if (patch->is_binary)
|
|
|
|
return apply_binary(state, img, patch);
|
|
|
|
|
|
|
|
while (frag) {
|
|
|
|
nth++;
|
|
|
|
if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) {
|
|
|
|
error(_("patch failed: %s:%ld"), name, frag->oldpos);
|
|
|
|
if (!state->apply_with_reject)
|
|
|
|
return -1;
|
|
|
|
frag->rejected = 1;
|
|
|
|
}
|
|
|
|
frag = frag->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-20 04:47:19 +08:00
|
|
|
static int read_blob_object(struct strbuf *buf, const struct object_id *oid, unsigned mode)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
if (S_ISGITLINK(mode)) {
|
|
|
|
strbuf_grow(buf, 100);
|
2016-09-20 04:47:19 +08:00
|
|
|
strbuf_addf(buf, "Subproject commit %s\n", oid_to_hex(oid));
|
2016-04-23 02:55:46 +08:00
|
|
|
} else {
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long sz;
|
|
|
|
char *result;
|
|
|
|
|
2023-03-28 21:58:50 +08:00
|
|
|
result = repo_read_object_file(the_repository, oid, &type,
|
|
|
|
&sz);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!result)
|
|
|
|
return -1;
|
|
|
|
/* XXX read_sha1_file NUL-terminates */
|
|
|
|
strbuf_attach(buf, result, sz, sz + 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf)
|
|
|
|
{
|
|
|
|
if (!ce)
|
|
|
|
return 0;
|
2016-09-20 04:47:19 +08:00
|
|
|
return read_blob_object(buf, &ce->oid, ce->ce_mode);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct patch *in_fn_table(struct apply_state *state, const char *name)
|
|
|
|
{
|
|
|
|
struct string_list_item *item;
|
|
|
|
|
2022-05-03 00:50:37 +08:00
|
|
|
if (!name)
|
2016-04-23 02:55:46 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
item = string_list_lookup(&state->fn_table, name);
|
2022-05-03 00:50:37 +08:00
|
|
|
if (item)
|
2016-04-23 02:55:46 +08:00
|
|
|
return (struct patch *)item->util;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* item->util in the filename table records the status of the path.
|
|
|
|
* Usually it points at a patch (whose result records the contents
|
|
|
|
* of it after applying it), but it could be PATH_WAS_DELETED for a
|
|
|
|
* path that a previously applied patch has already removed, or
|
|
|
|
* PATH_TO_BE_DELETED for a path that a later patch would remove.
|
|
|
|
*
|
|
|
|
* The latter is needed to deal with a case where two paths A and B
|
|
|
|
* are swapped by first renaming A to B and then renaming B to A;
|
|
|
|
* moving A to B should not be prevented due to presence of B as we
|
|
|
|
* will remove it in a later patch.
|
|
|
|
*/
|
|
|
|
#define PATH_TO_BE_DELETED ((struct patch *) -2)
|
|
|
|
#define PATH_WAS_DELETED ((struct patch *) -1)
|
|
|
|
|
|
|
|
static int to_be_deleted(struct patch *patch)
|
|
|
|
{
|
|
|
|
return patch == PATH_TO_BE_DELETED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int was_deleted(struct patch *patch)
|
|
|
|
{
|
|
|
|
return patch == PATH_WAS_DELETED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_to_fn_table(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
struct string_list_item *item;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always add new_name unless patch is a deletion
|
|
|
|
* This should cover the cases for normal diffs,
|
|
|
|
* file creations and copies
|
|
|
|
*/
|
2022-05-03 00:50:37 +08:00
|
|
|
if (patch->new_name) {
|
2016-04-23 02:55:46 +08:00
|
|
|
item = string_list_insert(&state->fn_table, patch->new_name);
|
|
|
|
item->util = patch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* store a failure on rename/deletion cases because
|
|
|
|
* later chunks shouldn't patch old names
|
|
|
|
*/
|
|
|
|
if ((patch->new_name == NULL) || (patch->is_rename)) {
|
|
|
|
item = string_list_insert(&state->fn_table, patch->old_name);
|
|
|
|
item->util = PATH_WAS_DELETED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prepare_fn_table(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* store information about incoming file deletion
|
|
|
|
*/
|
|
|
|
while (patch) {
|
|
|
|
if ((patch->new_name == NULL) || (patch->is_rename)) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
item = string_list_insert(&state->fn_table, patch->old_name);
|
|
|
|
item->util = PATH_TO_BE_DELETED;
|
|
|
|
}
|
|
|
|
patch = patch->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int checkout_target(struct index_state *istate,
|
|
|
|
struct cache_entry *ce, struct stat *st)
|
|
|
|
{
|
2016-09-23 00:11:33 +08:00
|
|
|
struct checkout costate = CHECKOUT_INIT;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
costate.refresh_cache = 1;
|
|
|
|
costate.istate = istate;
|
2018-11-14 02:28:00 +08:00
|
|
|
if (checkout_entry(ce, &costate, NULL, NULL) ||
|
|
|
|
lstat(ce->name, st))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("cannot checkout %s"), ce->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct patch *previous_patch(struct apply_state *state,
|
|
|
|
struct patch *patch,
|
|
|
|
int *gone)
|
|
|
|
{
|
|
|
|
struct patch *previous;
|
|
|
|
|
|
|
|
*gone = 0;
|
|
|
|
if (patch->is_copy || patch->is_rename)
|
|
|
|
return NULL; /* "git" patches do not depend on the order */
|
|
|
|
|
|
|
|
previous = in_fn_table(state, patch->old_name);
|
|
|
|
if (!previous)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (to_be_deleted(previous))
|
|
|
|
return NULL; /* the deletion hasn't happened yet */
|
|
|
|
|
|
|
|
if (was_deleted(previous))
|
|
|
|
*gone = 1;
|
|
|
|
|
|
|
|
return previous;
|
|
|
|
}
|
|
|
|
|
2018-08-14 00:14:38 +08:00
|
|
|
static int verify_index_match(struct apply_state *state,
|
|
|
|
const struct cache_entry *ce,
|
|
|
|
struct stat *st)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
if (S_ISGITLINK(ce->ce_mode)) {
|
|
|
|
if (!S_ISDIR(st->st_mode))
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
2018-08-14 00:14:40 +08:00
|
|
|
return ie_match_stat(state->repo->index, ce, st,
|
|
|
|
CE_MATCH_IGNORE_VALID | CE_MATCH_IGNORE_SKIP_WORKTREE);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define SUBMODULE_PATCH_WITHOUT_INDEX 1
|
|
|
|
|
|
|
|
static int load_patch_target(struct apply_state *state,
|
|
|
|
struct strbuf *buf,
|
|
|
|
const struct cache_entry *ce,
|
|
|
|
struct stat *st,
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
struct patch *patch,
|
2016-04-23 02:55:46 +08:00
|
|
|
const char *name,
|
|
|
|
unsigned expected_mode)
|
|
|
|
{
|
|
|
|
if (state->cached || state->check_index) {
|
|
|
|
if (read_file_or_gitlink(ce, buf))
|
|
|
|
return error(_("failed to read %s"), name);
|
|
|
|
} else if (name) {
|
|
|
|
if (S_ISGITLINK(expected_mode)) {
|
|
|
|
if (ce)
|
|
|
|
return read_file_or_gitlink(ce, buf);
|
|
|
|
else
|
|
|
|
return SUBMODULE_PATCH_WITHOUT_INDEX;
|
|
|
|
} else if (has_symlink_leading_path(name, strlen(name))) {
|
|
|
|
return error(_("reading from '%s' beyond a symbolic link"), name);
|
|
|
|
} else {
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
if (read_old_data(st, patch, name, buf))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("failed to read %s"), name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We are about to apply "patch"; populate the "image" with the
|
|
|
|
* current version we have, from the working tree or from the index,
|
|
|
|
* depending on the situation e.g. --cached/--index. If we are
|
|
|
|
* applying a non-git patch that incrementally updates the tree,
|
|
|
|
* we read from the result of a previous diff.
|
|
|
|
*/
|
|
|
|
static int load_preimage(struct apply_state *state,
|
|
|
|
struct image *image,
|
|
|
|
struct patch *patch, struct stat *st,
|
|
|
|
const struct cache_entry *ce)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
size_t len;
|
|
|
|
char *img;
|
|
|
|
struct patch *previous;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
previous = previous_patch(state, patch, &status);
|
|
|
|
if (status)
|
|
|
|
return error(_("path %s has been renamed/deleted"),
|
|
|
|
patch->old_name);
|
|
|
|
if (previous) {
|
|
|
|
/* We have a patched copy in memory; use that. */
|
|
|
|
strbuf_add(&buf, previous->result, previous->resultsize);
|
|
|
|
} else {
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
status = load_patch_target(state, &buf, ce, st, patch,
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->old_name, patch->old_mode);
|
|
|
|
if (status < 0)
|
|
|
|
return status;
|
|
|
|
else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) {
|
|
|
|
/*
|
|
|
|
* There is no way to apply subproject
|
|
|
|
* patch without looking at the index.
|
|
|
|
* NEEDSWORK: shouldn't this be flagged
|
|
|
|
* as an error???
|
|
|
|
*/
|
|
|
|
free_fragment_list(patch->fragments);
|
|
|
|
patch->fragments = NULL;
|
|
|
|
} else if (status) {
|
|
|
|
return error(_("failed to read %s"), patch->old_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
img = strbuf_detach(&buf, &len);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_prepare(image, img, len, !patch->is_binary);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-09-06 03:06:57 +08:00
|
|
|
static int resolve_to(struct image *image, const struct object_id *result_id)
|
|
|
|
{
|
|
|
|
unsigned long size;
|
|
|
|
enum object_type type;
|
2024-09-17 18:08:08 +08:00
|
|
|
char *data;
|
2021-09-06 03:06:57 +08:00
|
|
|
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(image);
|
2021-09-06 03:06:57 +08:00
|
|
|
|
2024-09-17 18:08:08 +08:00
|
|
|
data = repo_read_object_file(the_repository, result_id, &type, &size);
|
|
|
|
if (!data || type != OBJ_BLOB)
|
2021-09-06 03:06:57 +08:00
|
|
|
die("unable to read blob object %s", oid_to_hex(result_id));
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_attach(&image->buf, data, size, size + 1);
|
2021-09-06 03:06:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-21 23:57:27 +08:00
|
|
|
static int three_way_merge(struct apply_state *state,
|
|
|
|
struct image *image,
|
2016-04-23 02:55:46 +08:00
|
|
|
char *path,
|
2016-09-20 04:47:19 +08:00
|
|
|
const struct object_id *base,
|
|
|
|
const struct object_id *ours,
|
|
|
|
const struct object_id *theirs)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
mmfile_t base_file, our_file, their_file;
|
2024-09-09 22:10:58 +08:00
|
|
|
struct ll_merge_options merge_opts = LL_MERGE_OPTIONS_INIT;
|
2016-04-23 02:55:46 +08:00
|
|
|
mmbuffer_t result = { NULL };
|
2022-02-02 10:37:30 +08:00
|
|
|
enum ll_merge_result status;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2021-09-06 03:06:57 +08:00
|
|
|
/* resolve trivial cases first */
|
|
|
|
if (oideq(base, ours))
|
|
|
|
return resolve_to(image, theirs);
|
|
|
|
else if (oideq(base, theirs) || oideq(ours, theirs))
|
|
|
|
return resolve_to(image, ours);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
read_mmblob(&base_file, base);
|
|
|
|
read_mmblob(&our_file, ours);
|
|
|
|
read_mmblob(&their_file, theirs);
|
2024-09-09 22:10:58 +08:00
|
|
|
merge_opts.variant = state->merge_variant;
|
2016-04-23 02:55:46 +08:00
|
|
|
status = ll_merge(&result, path,
|
|
|
|
&base_file, "base",
|
|
|
|
&our_file, "ours",
|
2018-09-21 23:57:27 +08:00
|
|
|
&their_file, "theirs",
|
|
|
|
state->repo->index,
|
2024-09-09 22:10:58 +08:00
|
|
|
&merge_opts);
|
2022-02-02 10:37:30 +08:00
|
|
|
if (status == LL_MERGE_BINARY_CONFLICT)
|
|
|
|
warning("Cannot merge binary files: %s (%s vs. %s)",
|
|
|
|
path, "ours", "theirs");
|
2016-04-23 02:55:46 +08:00
|
|
|
free(base_file.ptr);
|
|
|
|
free(our_file.ptr);
|
|
|
|
free(their_file.ptr);
|
|
|
|
if (status < 0 || !result.ptr) {
|
|
|
|
free(result.ptr);
|
|
|
|
return -1;
|
|
|
|
}
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(image);
|
2024-09-17 18:08:08 +08:00
|
|
|
strbuf_attach(&image->buf, result.ptr, result.size, result.size);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When directly falling back to add/add three-way merge, we read from
|
|
|
|
* the current contents of the new_name. In no cases other than that
|
|
|
|
* this function will be called.
|
|
|
|
*/
|
|
|
|
static int load_current(struct apply_state *state,
|
|
|
|
struct image *image,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int status, pos;
|
|
|
|
size_t len;
|
|
|
|
char *img;
|
|
|
|
struct stat st;
|
|
|
|
struct cache_entry *ce;
|
|
|
|
char *name = patch->new_name;
|
|
|
|
unsigned mode = patch->new_mode;
|
|
|
|
|
|
|
|
if (!patch->is_new)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("patch to %s is not a creation", patch->old_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2018-08-14 00:14:40 +08:00
|
|
|
pos = index_name_pos(state->repo->index, name, strlen(name));
|
2016-04-23 02:55:46 +08:00
|
|
|
if (pos < 0)
|
|
|
|
return error(_("%s: does not exist in index"), name);
|
2018-08-14 00:14:40 +08:00
|
|
|
ce = state->repo->index->cache[pos];
|
2016-04-23 02:55:46 +08:00
|
|
|
if (lstat(name, &st)) {
|
|
|
|
if (errno != ENOENT)
|
2016-09-05 04:18:24 +08:00
|
|
|
return error_errno("%s", name);
|
2018-08-14 00:14:40 +08:00
|
|
|
if (checkout_target(state->repo->index, ce, &st))
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2018-08-14 00:14:38 +08:00
|
|
|
if (verify_index_match(state, ce, &st))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("%s: does not match index"), name);
|
|
|
|
|
apply: file commited with CRLF should roundtrip diff and apply
When a file had been commited with CRLF but now .gitattributes say
"* text=auto" (or core.autocrlf is true), the following does not
roundtrip, `git apply` fails:
printf "Added line\r\n" >>file &&
git diff >patch &&
git checkout -- . &&
git apply patch
Before applying the patch, the file from working tree is converted
into the index format (clean filter, CRLF conversion, ...). Here,
when commited with CRLF, the line endings should not be converted.
Note that `git apply --index` or `git apply --cache` doesn't call
convert_to_git() because the source material is already in index
format.
Analyze the patch if there is a) any context line with CRLF, or b)
if any line with CRLF is to be removed. In this case the patch file
`patch` has mixed line endings, for a) it looks like this:
diff --git a/one b/one
index 533790e..c30dea8 100644
--- a/one
+++ b/one
@@ -1 +1,2 @@
a\r
+b\r
And for b) it looks like this:
diff --git a/one b/one
index 533790e..485540d 100644
--- a/one
+++ b/one
@@ -1 +1 @@
-a\r
+b\r
If `git apply` detects that the patch itself has CRLF, (look at the
line " a\r" or "-a\r" above), the new flag crlf_in_old is set in
"struct patch" and two things will happen:
- read_old_data() will not convert CRLF into LF by calling
convert_to_git(..., SAFE_CRLF_KEEP_CRLF);
- The WS_CR_AT_EOL bit is set in the "white space rule",
CRLF are no longer treated as white space.
While at there, make it clear that read_old_data() in apply.c knows
what it wants convert_to_git() to do with respect to CRLF. In fact,
this codepath is about applying a patch to a file in the filesystem,
which may not exist in the index, or may exist but may not match
what is recorded in the index, or in the extreme case, we may not
even be in a Git repository. If convert_to_git() peeked at the
index while doing its work, it *would* be a bug.
Pass NULL instead of &the_index to convert_to_git() to make sure we
catch future bugs to clarify this.
Update the test in t4124: split one test case into 3:
- Detect the " a\r" line in the patch
- Detect the "-a\r" line in the patch
- Use LF in repo and CLRF in the worktree.
Reported-by: Anthony Sottile <asottile@umich.edu>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-19 19:28:01 +08:00
|
|
|
status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (status < 0)
|
|
|
|
return status;
|
|
|
|
else if (status)
|
|
|
|
return -1;
|
|
|
|
img = strbuf_detach(&buf, &len);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_prepare(image, img, len, !patch->is_binary);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int try_threeway(struct apply_state *state,
|
|
|
|
struct image *image,
|
|
|
|
struct patch *patch,
|
|
|
|
struct stat *st,
|
|
|
|
const struct cache_entry *ce)
|
|
|
|
{
|
2016-09-20 04:47:19 +08:00
|
|
|
struct object_id pre_oid, post_oid, our_oid;
|
2016-04-23 02:55:46 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
size_t len;
|
|
|
|
int status;
|
|
|
|
char *img;
|
2024-09-17 18:08:01 +08:00
|
|
|
struct image tmp_image = IMAGE_INIT;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* No point falling back to 3-way merge in these cases */
|
|
|
|
if (patch->is_delete ||
|
2021-12-18 07:29:02 +08:00
|
|
|
S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode) ||
|
|
|
|
(patch->is_new && !patch->direct_to_threeway) ||
|
|
|
|
(patch->is_rename && !patch->lines_added && !patch->lines_deleted))
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Preimage the patch was prepared for */
|
|
|
|
if (patch->is_new)
|
2022-02-05 07:48:26 +08:00
|
|
|
write_object_file("", 0, OBJ_BLOB, &pre_oid);
|
2023-03-28 21:58:46 +08:00
|
|
|
else if (repo_get_oid(the_repository, patch->old_oid_prefix, &pre_oid) ||
|
2016-09-20 04:47:19 +08:00
|
|
|
read_blob_object(&buf, &pre_oid, patch->old_mode))
|
2021-04-07 07:25:32 +08:00
|
|
|
return error(_("repository lacks the necessary blob to perform 3-way merge."));
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2021-04-29 10:35:03 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent && patch->direct_to_threeway)
|
2021-04-07 07:25:32 +08:00
|
|
|
fprintf(stderr, _("Performing three-way merge...\n"));
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
img = strbuf_detach(&buf, &len);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_prepare(&tmp_image, img, len, 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
/* Apply the patch to get the post image */
|
|
|
|
if (apply_fragments(state, &tmp_image, patch) < 0) {
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(&tmp_image);
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2016-09-20 04:47:19 +08:00
|
|
|
/* post_oid is theirs */
|
2024-09-17 18:08:08 +08:00
|
|
|
write_object_file(tmp_image.buf.buf, tmp_image.buf.len, OBJ_BLOB, &post_oid);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(&tmp_image);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2016-09-20 04:47:19 +08:00
|
|
|
/* our_oid is ours */
|
2016-04-23 02:55:46 +08:00
|
|
|
if (patch->is_new) {
|
|
|
|
if (load_current(state, &tmp_image, patch))
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("cannot read the current contents of '%s'"),
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->new_name);
|
|
|
|
} else {
|
|
|
|
if (load_preimage(state, &tmp_image, patch, st, ce))
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("cannot read the current contents of '%s'"),
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->old_name);
|
|
|
|
}
|
2024-09-17 18:08:08 +08:00
|
|
|
write_object_file(tmp_image.buf.buf, tmp_image.buf.len, OBJ_BLOB, &our_oid);
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(&tmp_image);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* in-core three-way merge between post and our using pre as base */
|
2018-09-21 23:57:27 +08:00
|
|
|
status = three_way_merge(state, image, patch->new_name,
|
2016-09-20 04:47:19 +08:00
|
|
|
&pre_oid, &our_oid, &post_oid);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (status < 0) {
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
fprintf(stderr,
|
2021-04-07 07:25:32 +08:00
|
|
|
_("Failed to perform three-way merge...\n"));
|
2016-04-23 02:55:46 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
patch->conflicted_threeway = 1;
|
|
|
|
if (patch->is_new)
|
2024-06-14 14:49:54 +08:00
|
|
|
oidclr(&patch->threeway_stage[0], the_repository->hash_algo);
|
2016-04-23 02:55:46 +08:00
|
|
|
else
|
2016-09-20 04:47:19 +08:00
|
|
|
oidcpy(&patch->threeway_stage[0], &pre_oid);
|
|
|
|
oidcpy(&patch->threeway_stage[1], &our_oid);
|
|
|
|
oidcpy(&patch->threeway_stage[2], &post_oid);
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
fprintf(stderr,
|
2016-10-14 19:43:36 +08:00
|
|
|
_("Applied patch to '%s' with conflicts.\n"),
|
2016-09-05 04:18:25 +08:00
|
|
|
patch->new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
} else {
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
fprintf(stderr,
|
2016-10-14 19:43:36 +08:00
|
|
|
_("Applied patch to '%s' cleanly.\n"),
|
2016-09-05 04:18:25 +08:00
|
|
|
patch->new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int apply_data(struct apply_state *state, struct patch *patch,
|
|
|
|
struct stat *st, const struct cache_entry *ce)
|
|
|
|
{
|
2024-09-17 18:08:01 +08:00
|
|
|
struct image image = IMAGE_INIT;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
if (load_preimage(state, &image, patch, st, ce) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2021-04-07 07:25:32 +08:00
|
|
|
if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) {
|
2021-04-29 10:35:03 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent &&
|
|
|
|
state->threeway && !patch->direct_to_threeway)
|
|
|
|
fprintf(stderr, _("Falling back to direct application...\n"));
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
/* Note: with --reject, apply_fragments() returns 0 */
|
2024-04-23 06:54:05 +08:00
|
|
|
if (patch->direct_to_threeway || apply_fragments(state, &image, patch) < 0) {
|
2024-09-17 18:07:55 +08:00
|
|
|
image_clear(&image);
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
2024-04-23 06:54:05 +08:00
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
2024-09-17 18:08:08 +08:00
|
|
|
patch->result = strbuf_detach(&image.buf, &patch->resultsize);
|
2016-04-23 02:55:46 +08:00
|
|
|
add_to_fn_table(state, patch);
|
2024-09-17 18:08:03 +08:00
|
|
|
free(image.line);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
if (0 < patch->is_delete && patch->resultsize)
|
|
|
|
return error(_("removal patch leaves file contents"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If "patch" that we are looking at modifies or deletes what we have,
|
|
|
|
* we would want it not to lose any local modification we have, either
|
|
|
|
* in the working tree or in the index.
|
|
|
|
*
|
|
|
|
* This also decides if a non-git patch is a creation patch or a
|
|
|
|
* modification to an existing empty file. We do not check the state
|
|
|
|
* of the current tree for a creation patch in this function; the caller
|
|
|
|
* check_patch() separately makes sure (and errors out otherwise) that
|
|
|
|
* the path the patch creates does not exist in the current tree.
|
|
|
|
*/
|
|
|
|
static int check_preimage(struct apply_state *state,
|
|
|
|
struct patch *patch,
|
|
|
|
struct cache_entry **ce,
|
|
|
|
struct stat *st)
|
|
|
|
{
|
|
|
|
const char *old_name = patch->old_name;
|
|
|
|
struct patch *previous = NULL;
|
|
|
|
int stat_ret = 0, status;
|
|
|
|
unsigned st_mode = 0;
|
|
|
|
|
|
|
|
if (!old_name)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
assert(patch->is_new <= 0);
|
|
|
|
previous = previous_patch(state, patch, &status);
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
return error(_("path %s has been renamed/deleted"), old_name);
|
|
|
|
if (previous) {
|
|
|
|
st_mode = previous->new_mode;
|
|
|
|
} else if (!state->cached) {
|
|
|
|
stat_ret = lstat(old_name, st);
|
|
|
|
if (stat_ret && errno != ENOENT)
|
2016-09-05 04:18:24 +08:00
|
|
|
return error_errno("%s", old_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state->check_index && !previous) {
|
2018-08-14 00:14:40 +08:00
|
|
|
int pos = index_name_pos(state->repo->index, old_name,
|
|
|
|
strlen(old_name));
|
2016-04-23 02:55:46 +08:00
|
|
|
if (pos < 0) {
|
|
|
|
if (patch->is_new < 0)
|
|
|
|
goto is_new;
|
|
|
|
return error(_("%s: does not exist in index"), old_name);
|
|
|
|
}
|
2018-08-14 00:14:40 +08:00
|
|
|
*ce = state->repo->index->cache[pos];
|
2016-04-23 02:55:46 +08:00
|
|
|
if (stat_ret < 0) {
|
2018-08-14 00:14:40 +08:00
|
|
|
if (checkout_target(state->repo->index, *ce, st))
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2018-08-14 00:14:38 +08:00
|
|
|
if (!state->cached && verify_index_match(state, *ce, st))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("%s: does not match index"), old_name);
|
|
|
|
if (state->cached)
|
|
|
|
st_mode = (*ce)->ce_mode;
|
|
|
|
} else if (stat_ret < 0) {
|
|
|
|
if (patch->is_new < 0)
|
|
|
|
goto is_new;
|
2016-09-05 04:18:24 +08:00
|
|
|
return error_errno("%s", old_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2023-12-27 07:32:16 +08:00
|
|
|
if (!state->cached && !previous) {
|
2023-12-27 07:32:18 +08:00
|
|
|
if (*ce && !(*ce)->ce_mode)
|
|
|
|
BUG("ce_mode == 0 for path '%s'", old_name);
|
|
|
|
|
|
|
|
if (trust_executable_bit)
|
2023-12-27 07:32:16 +08:00
|
|
|
st_mode = ce_mode_from_stat(*ce, st->st_mode);
|
2023-12-27 07:32:18 +08:00
|
|
|
else if (*ce)
|
|
|
|
st_mode = (*ce)->ce_mode;
|
|
|
|
else
|
|
|
|
st_mode = patch->old_mode;
|
2023-12-27 07:32:16 +08:00
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
if (patch->is_new < 0)
|
|
|
|
patch->is_new = 0;
|
|
|
|
if (!patch->old_mode)
|
|
|
|
patch->old_mode = st_mode;
|
|
|
|
if ((st_mode ^ patch->old_mode) & S_IFMT)
|
|
|
|
return error(_("%s: wrong type"), old_name);
|
|
|
|
if (st_mode != patch->old_mode)
|
|
|
|
warning(_("%s has type %o, expected %o"),
|
|
|
|
old_name, st_mode, patch->old_mode);
|
|
|
|
if (!patch->new_mode && !patch->is_delete)
|
|
|
|
patch->new_mode = st_mode;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
is_new:
|
|
|
|
patch->is_new = 1;
|
|
|
|
patch->is_delete = 0;
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(patch->old_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define EXISTS_IN_INDEX 1
|
|
|
|
#define EXISTS_IN_WORKTREE 2
|
2020-08-08 15:49:58 +08:00
|
|
|
#define EXISTS_IN_INDEX_AS_ITA 3
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
static int check_to_create(struct apply_state *state,
|
|
|
|
const char *new_name,
|
|
|
|
int ok_if_exists)
|
|
|
|
{
|
|
|
|
struct stat nst;
|
|
|
|
|
2020-08-08 15:49:58 +08:00
|
|
|
if (state->check_index && (!ok_if_exists || !state->cached)) {
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
pos = index_name_pos(state->repo->index, new_name, strlen(new_name));
|
|
|
|
if (pos >= 0) {
|
|
|
|
struct cache_entry *ce = state->repo->index->cache[pos];
|
|
|
|
|
|
|
|
/* allow ITA, as they do not yet exist in the index */
|
|
|
|
if (!ok_if_exists && !(ce->ce_flags & CE_INTENT_TO_ADD))
|
|
|
|
return EXISTS_IN_INDEX;
|
|
|
|
|
|
|
|
/* ITA entries can never match working tree files */
|
|
|
|
if (!state->cached && (ce->ce_flags & CE_INTENT_TO_ADD))
|
|
|
|
return EXISTS_IN_INDEX_AS_ITA;
|
|
|
|
}
|
2020-08-06 14:01:17 +08:00
|
|
|
}
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
if (state->cached)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!lstat(new_name, &nst)) {
|
|
|
|
if (S_ISDIR(nst.st_mode) || ok_if_exists)
|
|
|
|
return 0;
|
|
|
|
/*
|
|
|
|
* A leading component of new_name might be a symlink
|
|
|
|
* that is going to be removed with this patch, but
|
|
|
|
* still pointing at somewhere that has the path.
|
|
|
|
* In such a case, path "new_name" does not exist as
|
|
|
|
* far as git is concerned.
|
|
|
|
*/
|
|
|
|
if (has_symlink_leading_path(new_name, strlen(new_name)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return EXISTS_IN_WORKTREE;
|
2017-05-30 08:23:33 +08:00
|
|
|
} else if (!is_missing_file_error(errno)) {
|
2016-09-05 04:18:24 +08:00
|
|
|
return error_errno("%s", new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
for ( ; patch; patch = patch->next) {
|
|
|
|
if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
|
|
|
|
(patch->is_rename || patch->is_delete))
|
|
|
|
/* the symlink at patch->old_name is removed */
|
2022-01-07 20:16:53 +08:00
|
|
|
strset_add(&state->removed_symlinks, patch->old_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
if (patch->new_name && S_ISLNK(patch->new_mode))
|
|
|
|
/* the symlink at patch->new_name is created or remains */
|
2022-01-07 20:16:53 +08:00
|
|
|
strset_add(&state->kept_symlinks, patch->new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name)
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
while (--name->len && name->buf[name->len] != '/')
|
|
|
|
; /* scan backwards */
|
|
|
|
if (!name->len)
|
|
|
|
break;
|
|
|
|
name->buf[name->len] = '\0';
|
2022-01-07 20:16:53 +08:00
|
|
|
if (strset_contains(&state->kept_symlinks, name->buf))
|
2016-04-23 02:55:46 +08:00
|
|
|
return 1;
|
2022-01-07 20:16:53 +08:00
|
|
|
if (strset_contains(&state->removed_symlinks, name->buf))
|
2016-04-23 02:55:46 +08:00
|
|
|
/*
|
|
|
|
* This cannot be "return 0", because we may
|
|
|
|
* see a new one created at a higher level.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* otherwise, check the preimage */
|
|
|
|
if (state->check_index) {
|
|
|
|
struct cache_entry *ce;
|
|
|
|
|
2018-08-14 00:14:40 +08:00
|
|
|
ce = index_file_exists(state->repo->index, name->buf,
|
|
|
|
name->len, ignore_case);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (ce && S_ISLNK(ce->ce_mode))
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
struct stat st;
|
|
|
|
if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int path_is_beyond_symlink(struct apply_state *state, const char *name_)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct strbuf name = STRBUF_INIT;
|
|
|
|
|
|
|
|
assert(*name_ != '\0');
|
|
|
|
strbuf_addstr(&name, name_);
|
|
|
|
ret = path_is_beyond_symlink_1(state, &name);
|
|
|
|
strbuf_release(&name);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_unsafe_path(struct patch *patch)
|
|
|
|
{
|
|
|
|
const char *old_name = NULL;
|
|
|
|
const char *new_name = NULL;
|
|
|
|
if (patch->is_delete)
|
|
|
|
old_name = patch->old_name;
|
|
|
|
else if (!patch->is_new && !patch->is_copy)
|
|
|
|
old_name = patch->old_name;
|
|
|
|
if (!patch->is_delete)
|
|
|
|
new_name = patch->new_name;
|
|
|
|
|
verify_path: disallow symlinks in .gitmodules
There are a few reasons it's not a good idea to make
.gitmodules a symlink, including:
1. It won't be portable to systems without symlinks.
2. It may behave inconsistently, since Git may look at
this file in the index or a tree without bothering to
resolve any symbolic links. We don't do this _yet_, but
the config infrastructure is there and it's planned for
the future.
With some clever code, we could make (2) work. And some
people may not care about (1) if they only work on one
platform. But there are a few security reasons to simply
disallow it:
a. A symlinked .gitmodules file may circumvent any fsck
checks of the content.
b. Git may read and write from the on-disk file without
sanity checking the symlink target. So for example, if
you link ".gitmodules" to "../oops" and run "git
submodule add", we'll write to the file "oops" outside
the repository.
Again, both of those are problems that _could_ be solved
with sufficient code, but given the complications in (1) and
(2), we're better off just outlawing it explicitly.
Note the slightly tricky call to verify_path() in
update-index's update_one(). There we may not have a mode if
we're not updating from the filesystem (e.g., we might just
be removing the file). Passing "0" as the mode there works
fine; since it's not a symlink, we'll just skip the extra
checks.
Signed-off-by: Jeff King <peff@peff.net>
2018-05-05 08:03:35 +08:00
|
|
|
if (old_name && !verify_path(old_name, patch->old_mode))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("invalid path '%s'"), old_name);
|
verify_path: disallow symlinks in .gitmodules
There are a few reasons it's not a good idea to make
.gitmodules a symlink, including:
1. It won't be portable to systems without symlinks.
2. It may behave inconsistently, since Git may look at
this file in the index or a tree without bothering to
resolve any symbolic links. We don't do this _yet_, but
the config infrastructure is there and it's planned for
the future.
With some clever code, we could make (2) work. And some
people may not care about (1) if they only work on one
platform. But there are a few security reasons to simply
disallow it:
a. A symlinked .gitmodules file may circumvent any fsck
checks of the content.
b. Git may read and write from the on-disk file without
sanity checking the symlink target. So for example, if
you link ".gitmodules" to "../oops" and run "git
submodule add", we'll write to the file "oops" outside
the repository.
Again, both of those are problems that _could_ be solved
with sufficient code, but given the complications in (1) and
(2), we're better off just outlawing it explicitly.
Note the slightly tricky call to verify_path() in
update-index's update_one(). There we may not have a mode if
we're not updating from the filesystem (e.g., we might just
be removing the file). Passing "0" as the mode there works
fine; since it's not a symlink, we'll just skip the extra
checks.
Signed-off-by: Jeff King <peff@peff.net>
2018-05-05 08:03:35 +08:00
|
|
|
if (new_name && !verify_path(new_name, patch->new_mode))
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("invalid path '%s'"), new_name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check and apply the patch in-core; leave the result in patch->result
|
|
|
|
* for the caller to write it out to the final destination.
|
|
|
|
*/
|
|
|
|
static int check_patch(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
const char *old_name = patch->old_name;
|
|
|
|
const char *new_name = patch->new_name;
|
|
|
|
const char *name = old_name ? old_name : new_name;
|
|
|
|
struct cache_entry *ce = NULL;
|
|
|
|
struct patch *tpatch;
|
|
|
|
int ok_if_exists;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
patch->rejected = 1; /* we will drop this after we succeed */
|
|
|
|
|
|
|
|
status = check_preimage(state, patch, &ce, &st);
|
|
|
|
if (status)
|
|
|
|
return status;
|
|
|
|
old_name = patch->old_name;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A type-change diff is always split into a patch to delete
|
|
|
|
* old, immediately followed by a patch to create new (see
|
|
|
|
* diff.c::run_diff()); in such a case it is Ok that the entry
|
|
|
|
* to be deleted by the previous patch is still in the working
|
|
|
|
* tree and in the index.
|
|
|
|
*
|
|
|
|
* A patch to swap-rename between A and B would first rename A
|
|
|
|
* to B and then rename B to A. While applying the first one,
|
|
|
|
* the presence of B should not stop A from getting renamed to
|
|
|
|
* B; ask to_be_deleted() about the later rename. Removal of
|
|
|
|
* B and rename from A to B is handled the same way by asking
|
|
|
|
* was_deleted().
|
|
|
|
*/
|
|
|
|
if ((tpatch = in_fn_table(state, new_name)) &&
|
|
|
|
(was_deleted(tpatch) || to_be_deleted(tpatch)))
|
|
|
|
ok_if_exists = 1;
|
|
|
|
else
|
|
|
|
ok_if_exists = 0;
|
|
|
|
|
|
|
|
if (new_name &&
|
|
|
|
((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
|
|
|
|
int err = check_to_create(state, new_name, ok_if_exists);
|
|
|
|
|
|
|
|
if (err && state->threeway) {
|
|
|
|
patch->direct_to_threeway = 1;
|
|
|
|
} else switch (err) {
|
|
|
|
case 0:
|
|
|
|
break; /* happy */
|
|
|
|
case EXISTS_IN_INDEX:
|
|
|
|
return error(_("%s: already exists in index"), new_name);
|
2020-08-08 15:49:58 +08:00
|
|
|
case EXISTS_IN_INDEX_AS_ITA:
|
|
|
|
return error(_("%s: does not match index"), new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
case EXISTS_IN_WORKTREE:
|
|
|
|
return error(_("%s: already exists in working directory"),
|
|
|
|
new_name);
|
|
|
|
default:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!patch->new_mode) {
|
|
|
|
if (0 < patch->is_new)
|
|
|
|
patch->new_mode = S_IFREG | 0644;
|
|
|
|
else
|
|
|
|
patch->new_mode = patch->old_mode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_name && old_name) {
|
|
|
|
int same = !strcmp(old_name, new_name);
|
|
|
|
if (!patch->new_mode)
|
|
|
|
patch->new_mode = patch->old_mode;
|
|
|
|
if ((patch->old_mode ^ patch->new_mode) & S_IFMT) {
|
|
|
|
if (same)
|
|
|
|
return error(_("new mode (%o) of %s does not "
|
|
|
|
"match old mode (%o)"),
|
|
|
|
patch->new_mode, new_name,
|
|
|
|
patch->old_mode);
|
|
|
|
else
|
|
|
|
return error(_("new mode (%o) of %s does not "
|
|
|
|
"match old mode (%o) of %s"),
|
|
|
|
patch->new_mode, new_name,
|
|
|
|
patch->old_mode, old_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!state->unsafe_paths && check_unsafe_path(patch))
|
|
|
|
return -128;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* An attempt to read from or delete a path that is beyond a
|
|
|
|
* symbolic link will be prevented by load_patch_target() that
|
|
|
|
* is called at the beginning of apply_data() so we do not
|
|
|
|
* have to worry about a patch marked with "is_delete" bit
|
|
|
|
* here. We however need to make sure that the patch result
|
|
|
|
* is not deposited to a path that is beyond a symbolic link
|
|
|
|
* here.
|
|
|
|
*/
|
|
|
|
if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name))
|
|
|
|
return error(_("affected file '%s' is beyond a symbolic link"),
|
|
|
|
patch->new_name);
|
|
|
|
|
|
|
|
if (apply_data(state, patch, &st, ce) < 0)
|
|
|
|
return error(_("%s: patch does not apply"), name);
|
|
|
|
patch->rejected = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_patch_list(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
prepare_symlink_changes(state, patch);
|
|
|
|
prepare_fn_table(state, patch);
|
|
|
|
while (patch) {
|
|
|
|
int res;
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_normal)
|
2016-04-23 02:55:46 +08:00
|
|
|
say_patch_name(stderr,
|
|
|
|
_("Checking patch %s..."), patch);
|
|
|
|
res = check_patch(state, patch);
|
|
|
|
if (res == -128)
|
|
|
|
return -128;
|
|
|
|
err |= res;
|
|
|
|
patch = patch->next;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:32 +08:00
|
|
|
static int read_apply_cache(struct apply_state *state)
|
|
|
|
{
|
|
|
|
if (state->index_file)
|
2018-08-14 00:14:40 +08:00
|
|
|
return read_index_from(state->repo->index, state->index_file,
|
2024-09-12 19:29:24 +08:00
|
|
|
repo_get_git_dir(the_repository));
|
2016-09-05 04:18:32 +08:00
|
|
|
else
|
2019-01-12 10:13:26 +08:00
|
|
|
return repo_read_index(state->repo);
|
2016-09-05 04:18:32 +08:00
|
|
|
}
|
|
|
|
|
2016-09-20 04:47:19 +08:00
|
|
|
/* This function tries to read the object name from the current index */
|
|
|
|
static int get_current_oid(struct apply_state *state, const char *path,
|
|
|
|
struct object_id *oid)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
int pos;
|
|
|
|
|
2016-09-05 04:18:32 +08:00
|
|
|
if (read_apply_cache(state) < 0)
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
2018-08-14 00:14:40 +08:00
|
|
|
pos = index_name_pos(state->repo->index, path, strlen(path));
|
2016-04-23 02:55:46 +08:00
|
|
|
if (pos < 0)
|
|
|
|
return -1;
|
2018-08-14 00:14:40 +08:00
|
|
|
oidcpy(oid, &state->repo->index->cache[pos]->oid);
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-20 04:47:19 +08:00
|
|
|
static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* A usable gitlink patch has only one fragment (hunk) that looks like:
|
|
|
|
* @@ -1 +1 @@
|
|
|
|
* -Subproject commit <old sha1>
|
|
|
|
* +Subproject commit <new sha1>
|
|
|
|
* or
|
|
|
|
* @@ -1 +0,0 @@
|
|
|
|
* -Subproject commit <old sha1>
|
|
|
|
* for a removal patch.
|
|
|
|
*/
|
|
|
|
struct fragment *hunk = p->fragments;
|
|
|
|
static const char heading[] = "-Subproject commit ";
|
|
|
|
char *preimage;
|
|
|
|
|
|
|
|
if (/* does the patch have only one hunk? */
|
|
|
|
hunk && !hunk->next &&
|
|
|
|
/* is its preimage one line? */
|
|
|
|
hunk->oldpos == 1 && hunk->oldlines == 1 &&
|
|
|
|
/* does preimage begin with the heading? */
|
|
|
|
(preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
|
|
|
|
starts_with(++preimage, heading) &&
|
|
|
|
/* does it record full SHA-1? */
|
2016-09-20 04:47:19 +08:00
|
|
|
!get_oid_hex(preimage + sizeof(heading) - 1, oid) &&
|
2018-10-15 08:01:59 +08:00
|
|
|
preimage[sizeof(heading) + the_hash_algo->hexsz - 1] == '\n' &&
|
2016-04-23 02:55:46 +08:00
|
|
|
/* does the abbreviated name on the index line agree with it? */
|
2018-10-15 08:02:00 +08:00
|
|
|
starts_with(preimage + sizeof(heading) - 1, p->old_oid_prefix))
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0; /* it all looks fine */
|
|
|
|
|
|
|
|
/* we may have full object name on the index line */
|
2018-10-15 08:02:00 +08:00
|
|
|
return get_oid_hex(p->old_oid_prefix, oid);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
2018-06-07 13:05:25 +08:00
|
|
|
/* Build an index that contains just the files needed for a 3way merge */
|
2016-09-05 04:18:31 +08:00
|
|
|
static int build_fake_ancestor(struct apply_state *state, struct patch *list)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct patch *patch;
|
treewide: always have a valid "index_state.repo" member
When the "repo" member was added to "the_index" in [1] the
repo_read_index() was made to populate it, but the unpopulated
"the_index" variable didn't get the same treatment.
Let's do that in initialize_the_repository() when we set it up, and
likewise for all of the current callers initialized an empty "struct
index_state".
This simplifies code that needs to deal with "the_index" or a custom
"struct index_state", we no longer need to second-guess this part of
the "index_state" deep in the stack. A recent example of such
second-guessing is the "istate->repo ? istate->repo : the_repository"
code in [2]. We can now simply use "istate->repo".
We're doing this by making use of the INDEX_STATE_INIT() macro (and
corresponding function) added in [3], which now have mandatory "repo"
arguments.
Because we now call index_state_init() in repository.c's
initialize_the_repository() we don't need to handle the case where we
have a "repo->index" whose "repo" member doesn't match the "repo"
we're setting up, i.e. the "Complete the double-reference" code in
repo_read_index() being altered here. That logic was originally added
in [1], and was working around the lack of what we now have in
initialize_the_repository().
For "fsmonitor-settings.c" we can remove the initialization of a NULL
"r" argument to "the_repository". This was added back in [4], and was
needed at the time for callers that would pass us the "r" from an
"istate->repo". Before this change such a change to
"fsmonitor-settings.c" would segfault all over the test suite (e.g. in
t0002-gitfile.sh).
This change has wider eventual implications for
"fsmonitor-settings.c". The reason the other lazy loading behavior in
it is required (starting with "if (!r->settings.fsmonitor) ..." is
because of the previously passed "r" being "NULL".
I have other local changes on top of this which move its configuration
reading to "prepare_repo_settings()" in "repo-settings.c", as we could
now start to rely on it being called for our "r". But let's leave all
of that for now, and narrowly remove this particular part of the
lazy-loading.
1. 1fd9ae517c4 (repository: add repo reference to index_state,
2021-01-23)
2. ee1f0c242ef (read-cache: add index.skipHash config option,
2023-01-06)
3. 2f6b1eb794e (cache API: add a "INDEX_STATE_INIT" macro/function,
add release_index(), 2023-01-12)
4. 1e0ea5c4316 (fsmonitor: config settings are repository-specific,
2022-03-25)
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Acked-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-01-17 21:57:00 +08:00
|
|
|
struct index_state result = INDEX_STATE_INIT(state->repo);
|
2018-05-10 04:55:38 +08:00
|
|
|
struct lock_file lock = LOCK_INIT;
|
2016-04-23 02:55:46 +08:00
|
|
|
int res;
|
|
|
|
|
|
|
|
/* Once we start supporting the reverse patch, it may be
|
|
|
|
* worth showing the new sha1 prefix, but until then...
|
|
|
|
*/
|
|
|
|
for (patch = list; patch; patch = patch->next) {
|
2016-09-20 04:47:19 +08:00
|
|
|
struct object_id oid;
|
2016-04-23 02:55:46 +08:00
|
|
|
struct cache_entry *ce;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
name = patch->old_name ? patch->old_name : patch->new_name;
|
|
|
|
if (0 < patch->is_new)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (S_ISGITLINK(patch->old_mode)) {
|
2016-09-20 04:47:19 +08:00
|
|
|
if (!preimage_oid_in_gitlink_patch(patch, &oid))
|
2016-04-23 02:55:46 +08:00
|
|
|
; /* ok, the textual part looks sane */
|
|
|
|
else
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("sha1 information is lacking or "
|
|
|
|
"useless for submodule %s"), name);
|
2023-03-28 21:58:46 +08:00
|
|
|
} else if (!repo_get_oid_blob(the_repository, patch->old_oid_prefix, &oid)) {
|
2016-04-23 02:55:46 +08:00
|
|
|
; /* ok */
|
|
|
|
} else if (!patch->lines_added && !patch->lines_deleted) {
|
|
|
|
/* mode-only change: update the current */
|
2016-09-20 04:47:19 +08:00
|
|
|
if (get_current_oid(state, patch->old_name, &oid))
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("mode change for %s, which is not "
|
|
|
|
"in current HEAD"), name);
|
2016-04-23 02:55:46 +08:00
|
|
|
} else
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("sha1 information is lacking or useless "
|
|
|
|
"(%s)."), name);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2018-07-03 03:49:31 +08:00
|
|
|
ce = make_cache_entry(&result, patch->old_mode, &oid, name, 0, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!ce)
|
|
|
|
return error(_("make_cache_entry failed for path '%s'"),
|
|
|
|
name);
|
|
|
|
if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) {
|
2018-07-03 03:49:31 +08:00
|
|
|
discard_cache_entry(ce);
|
2016-10-14 19:43:37 +08:00
|
|
|
return error(_("could not add %s to temporary index"),
|
2016-04-23 02:55:46 +08:00
|
|
|
name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:31 +08:00
|
|
|
hold_lock_file_for_update(&lock, state->fake_ancestor, LOCK_DIE_ON_ERROR);
|
2016-04-23 02:55:46 +08:00
|
|
|
res = write_locked_index(&result, &lock, COMMIT_LOCK);
|
|
|
|
discard_index(&result);
|
|
|
|
|
2017-05-09 10:30:24 +08:00
|
|
|
if (res)
|
|
|
|
return error(_("could not write temporary index to %s"),
|
|
|
|
state->fake_ancestor);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2017-05-09 10:30:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2017-05-09 10:30:24 +08:00
|
|
|
static void stat_patch_list(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
int files, adds, dels;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2017-05-09 10:30:24 +08:00
|
|
|
for (files = adds = dels = 0 ; patch ; patch = patch->next) {
|
|
|
|
files++;
|
|
|
|
adds += patch->lines_added;
|
|
|
|
dels += patch->lines_deleted;
|
|
|
|
show_stats(state, patch);
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2017-05-09 10:30:24 +08:00
|
|
|
print_stat_summary(stdout, files, adds, dels);
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2017-05-09 10:30:24 +08:00
|
|
|
static void numstat_patch_list(struct apply_state *state,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
for ( ; patch; patch = patch->next) {
|
|
|
|
const char *name;
|
|
|
|
name = patch->new_name ? patch->new_name : patch->old_name;
|
|
|
|
if (patch->is_binary)
|
|
|
|
printf("-\t-\t");
|
|
|
|
else
|
|
|
|
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
|
|
|
|
write_name_quoted(name, stdout, state->line_termination);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
|
|
|
|
{
|
|
|
|
if (mode)
|
|
|
|
printf(" %s mode %06o %s\n", newdelete, mode, name);
|
|
|
|
else
|
|
|
|
printf(" %s %s\n", newdelete, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_mode_change(struct patch *p, int show_name)
|
|
|
|
{
|
|
|
|
if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
|
|
|
|
if (show_name)
|
|
|
|
printf(" mode change %06o => %06o %s\n",
|
|
|
|
p->old_mode, p->new_mode, p->new_name);
|
|
|
|
else
|
|
|
|
printf(" mode change %06o => %06o\n",
|
|
|
|
p->old_mode, p->new_mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_rename_copy(struct patch *p)
|
|
|
|
{
|
|
|
|
const char *renamecopy = p->is_rename ? "rename" : "copy";
|
2018-02-15 02:59:30 +08:00
|
|
|
const char *old_name, *new_name;
|
2017-05-09 10:30:24 +08:00
|
|
|
|
|
|
|
/* Find common prefix */
|
2018-02-15 02:59:30 +08:00
|
|
|
old_name = p->old_name;
|
|
|
|
new_name = p->new_name;
|
2017-05-09 10:30:24 +08:00
|
|
|
while (1) {
|
|
|
|
const char *slash_old, *slash_new;
|
2018-02-15 02:59:30 +08:00
|
|
|
slash_old = strchr(old_name, '/');
|
|
|
|
slash_new = strchr(new_name, '/');
|
2017-05-09 10:30:24 +08:00
|
|
|
if (!slash_old ||
|
|
|
|
!slash_new ||
|
2018-02-15 02:59:30 +08:00
|
|
|
slash_old - old_name != slash_new - new_name ||
|
|
|
|
memcmp(old_name, new_name, slash_new - new_name))
|
2017-05-09 10:30:24 +08:00
|
|
|
break;
|
2018-02-15 02:59:30 +08:00
|
|
|
old_name = slash_old + 1;
|
|
|
|
new_name = slash_new + 1;
|
2017-05-09 10:30:24 +08:00
|
|
|
}
|
2019-11-06 01:07:23 +08:00
|
|
|
/* p->old_name through old_name is the common prefix, and old_name and
|
|
|
|
* new_name through the end of names are renames
|
2017-05-09 10:30:24 +08:00
|
|
|
*/
|
2018-02-15 02:59:30 +08:00
|
|
|
if (old_name != p->old_name)
|
2017-05-09 10:30:24 +08:00
|
|
|
printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
|
2018-02-15 02:59:30 +08:00
|
|
|
(int)(old_name - p->old_name), p->old_name,
|
|
|
|
old_name, new_name, p->score);
|
2017-05-09 10:30:24 +08:00
|
|
|
else
|
|
|
|
printf(" %s %s => %s (%d%%)\n", renamecopy,
|
|
|
|
p->old_name, p->new_name, p->score);
|
|
|
|
show_mode_change(p, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void summary_patch_list(struct patch *patch)
|
|
|
|
{
|
|
|
|
struct patch *p;
|
|
|
|
|
|
|
|
for (p = patch; p; p = p->next) {
|
|
|
|
if (p->is_new)
|
|
|
|
show_file_mode_name("create", p->new_mode, p->new_name);
|
|
|
|
else if (p->is_delete)
|
|
|
|
show_file_mode_name("delete", p->old_mode, p->old_name);
|
|
|
|
else {
|
|
|
|
if (p->is_rename || p->is_copy)
|
|
|
|
show_rename_copy(p);
|
|
|
|
else {
|
|
|
|
if (p->score) {
|
|
|
|
printf(" rewrite %s (%d%%)\n",
|
|
|
|
p->new_name, p->score);
|
|
|
|
show_mode_change(p, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
show_mode_change(p, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void patch_stats(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
int lines = patch->lines_added + patch->lines_deleted;
|
|
|
|
|
|
|
|
if (lines > state->max_change)
|
|
|
|
state->max_change = lines;
|
|
|
|
if (patch->old_name) {
|
|
|
|
int len = quote_c_style(patch->old_name, NULL, NULL, 0);
|
|
|
|
if (!len)
|
|
|
|
len = strlen(patch->old_name);
|
|
|
|
if (len > state->max_len)
|
|
|
|
state->max_len = len;
|
|
|
|
}
|
|
|
|
if (patch->new_name) {
|
|
|
|
int len = quote_c_style(patch->new_name, NULL, NULL, 0);
|
|
|
|
if (!len)
|
|
|
|
len = strlen(patch->new_name);
|
|
|
|
if (len > state->max_len)
|
|
|
|
state->max_len = len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
|
|
|
|
{
|
2018-05-26 20:08:46 +08:00
|
|
|
if (state->update_index && !state->ita_only) {
|
2018-08-14 00:14:40 +08:00
|
|
|
if (remove_file_from_index(state->repo->index, patch->old_name) < 0)
|
2017-05-09 10:30:24 +08:00
|
|
|
return error(_("unable to remove %s from index"), patch->old_name);
|
|
|
|
}
|
|
|
|
if (!state->cached) {
|
|
|
|
if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
|
|
|
|
remove_path(patch->old_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_index_file(struct apply_state *state,
|
|
|
|
const char *path,
|
|
|
|
unsigned mode,
|
|
|
|
void *buf,
|
|
|
|
unsigned long size)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
struct cache_entry *ce;
|
|
|
|
int namelen = strlen(path);
|
|
|
|
|
2018-08-14 00:14:40 +08:00
|
|
|
ce = make_empty_cache_entry(state->repo->index, namelen);
|
2017-05-09 10:30:24 +08:00
|
|
|
memcpy(ce->name, path, namelen);
|
|
|
|
ce->ce_mode = create_ce_mode(mode);
|
|
|
|
ce->ce_flags = create_ce_flags(0);
|
|
|
|
ce->ce_namelen = namelen;
|
2018-05-26 20:08:46 +08:00
|
|
|
if (state->ita_only) {
|
|
|
|
ce->ce_flags |= CE_INTENT_TO_ADD;
|
|
|
|
set_object_name_for_intent_to_add_entry(ce);
|
|
|
|
} else if (S_ISGITLINK(mode)) {
|
2017-05-09 10:30:24 +08:00
|
|
|
const char *s;
|
|
|
|
|
|
|
|
if (!skip_prefix(buf, "Subproject commit ", &s) ||
|
|
|
|
get_oid_hex(s, &ce->oid)) {
|
2018-07-03 03:49:31 +08:00
|
|
|
discard_cache_entry(ce);
|
|
|
|
return error(_("corrupt patch for submodule %s"), path);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!state->cached) {
|
|
|
|
if (lstat(path, &st) < 0) {
|
2018-07-03 03:49:31 +08:00
|
|
|
discard_cache_entry(ce);
|
2016-09-05 04:18:24 +08:00
|
|
|
return error_errno(_("unable to stat newly "
|
|
|
|
"created file '%s'"),
|
|
|
|
path);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
2019-05-24 20:23:47 +08:00
|
|
|
fill_stat_cache_info(state->repo->index, ce, &st);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
2022-02-05 07:48:26 +08:00
|
|
|
if (write_object_file(buf, size, OBJ_BLOB, &ce->oid) < 0) {
|
2018-07-03 03:49:31 +08:00
|
|
|
discard_cache_entry(ce);
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("unable to create backing store "
|
|
|
|
"for newly created file %s"), path);
|
|
|
|
}
|
|
|
|
}
|
2018-08-14 00:14:40 +08:00
|
|
|
if (add_index_entry(state->repo->index, ce, ADD_CACHE_OK_TO_ADD) < 0) {
|
2018-07-03 03:49:31 +08:00
|
|
|
discard_cache_entry(ce);
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("unable to add cache entry for %s"), path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns:
|
|
|
|
* -1 if an unrecoverable error happened
|
|
|
|
* 0 if everything went well
|
|
|
|
* 1 if a recoverable error happened
|
|
|
|
*/
|
2018-08-14 00:14:38 +08:00
|
|
|
static int try_create_file(struct apply_state *state, const char *path,
|
|
|
|
unsigned int mode, const char *buf,
|
|
|
|
unsigned long size)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
int fd, res;
|
|
|
|
struct strbuf nbuf = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (S_ISGITLINK(mode)) {
|
|
|
|
struct stat st;
|
|
|
|
if (!lstat(path, &st) && S_ISDIR(st.st_mode))
|
|
|
|
return 0;
|
|
|
|
return !!mkdir(path, 0777);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_symlinks && S_ISLNK(mode))
|
|
|
|
/* Although buf:size is counted string, it also is NUL
|
|
|
|
* terminated.
|
|
|
|
*/
|
|
|
|
return !!symlink(buf, path);
|
|
|
|
|
|
|
|
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
|
|
|
|
if (fd < 0)
|
|
|
|
return 1;
|
|
|
|
|
convert: permit passing additional metadata to filter processes
There are a variety of situations where a filter process can make use of
some additional metadata. For example, some people find the ident
filter too limiting and would like to include the commit or the branch
in their smudged files. This information isn't available during
checkout as HEAD hasn't been updated at that point, and it wouldn't be
available in archives either.
Let's add a way to pass this metadata down to the filter. We pass the
blob we're operating on, the treeish (preferring the commit over the
tree if one exists), and the ref we're operating on. Note that we won't
pass this information in all cases, such as when renormalizing or when
we're performing diffs, since it doesn't make sense in those cases.
The data we currently get from the filter process looks like the
following:
command=smudge
pathname=git.c
0000
With this change, we'll get data more like this:
command=smudge
pathname=git.c
refname=refs/tags/v2.25.1
treeish=c522f061d551c9bb8684a7c3859b2ece4499b56b
blob=7be7ad34bd053884ec48923706e70c81719a8660
0000
There are a couple things to note about this approach. For operations
like checkout, treeish will always be a commit, since we cannot check
out individual trees, but for other operations, like archive, we can end
up operating on only a particular tree, so we'll provide only a tree as
the treeish. Similar comments apply for refname, since there are a
variety of cases in which we won't have a ref.
This commit wires up the code to print this information, but doesn't
pass any of it at this point. In a future commit, we'll have various
code paths pass the actual useful data down.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-03-17 02:05:02 +08:00
|
|
|
if (convert_to_working_tree(state->repo->index, path, buf, size, &nbuf, NULL)) {
|
2016-04-23 02:55:46 +08:00
|
|
|
size = nbuf.len;
|
|
|
|
buf = nbuf.buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = write_in_full(fd, buf, size) < 0;
|
|
|
|
if (res)
|
|
|
|
error_errno(_("failed to write to '%s'"), path);
|
|
|
|
strbuf_release(&nbuf);
|
|
|
|
|
|
|
|
if (close(fd) < 0 && !res)
|
|
|
|
return error_errno(_("closing file '%s'"), path);
|
|
|
|
|
|
|
|
return res ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We optimistically assume that the directories exist,
|
|
|
|
* which is true 99% of the time anyway. If they don't,
|
|
|
|
* we create them and try again.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* -1 on error
|
|
|
|
* 0 otherwise
|
|
|
|
*/
|
|
|
|
static int create_one_file(struct apply_state *state,
|
|
|
|
char *path,
|
|
|
|
unsigned mode,
|
|
|
|
const char *buf,
|
|
|
|
unsigned long size)
|
|
|
|
{
|
2024-04-05 18:53:23 +08:00
|
|
|
char *newpath = NULL;
|
2016-04-23 02:55:46 +08:00
|
|
|
int res;
|
|
|
|
|
|
|
|
if (state->cached)
|
|
|
|
return 0;
|
|
|
|
|
2023-02-02 18:54:34 +08:00
|
|
|
/*
|
|
|
|
* We already try to detect whether files are beyond a symlink in our
|
|
|
|
* up-front checks. But in the case where symlinks are created by any
|
|
|
|
* of the intermediate hunks it can happen that our up-front checks
|
|
|
|
* didn't yet see the symlink, but at the point of arriving here there
|
|
|
|
* in fact is one. We thus repeat the check for symlinks here.
|
|
|
|
*
|
|
|
|
* Note that this does not make the up-front check obsolete as the
|
|
|
|
* failure mode is different:
|
|
|
|
*
|
|
|
|
* - The up-front checks cause us to abort before we have written
|
|
|
|
* anything into the working directory. So when we exit this way the
|
|
|
|
* working directory remains clean.
|
|
|
|
*
|
|
|
|
* - The checks here happen in the middle of the action where we have
|
|
|
|
* already started to apply the patch. The end result will be a dirty
|
|
|
|
* working directory.
|
|
|
|
*
|
|
|
|
* Ideally, we should update the up-front checks to catch what would
|
|
|
|
* happen when we apply the patch before we damage the working tree.
|
|
|
|
* We have all the information necessary to do so. But for now, as a
|
|
|
|
* part of embargoed security work, having this check would serve as a
|
|
|
|
* reasonable first step.
|
|
|
|
*/
|
|
|
|
if (path_is_beyond_symlink(state, path))
|
|
|
|
return error(_("affected file '%s' is beyond a symbolic link"), path);
|
|
|
|
|
2018-08-14 00:14:38 +08:00
|
|
|
res = try_create_file(state, path, mode, buf, size);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res < 0)
|
|
|
|
return -1;
|
|
|
|
if (!res)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (errno == ENOENT) {
|
2020-12-02 07:45:04 +08:00
|
|
|
if (safe_create_leading_directories_no_share(path))
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
2018-08-14 00:14:38 +08:00
|
|
|
res = try_create_file(state, path, mode, buf, size);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res < 0)
|
|
|
|
return -1;
|
|
|
|
if (!res)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errno == EEXIST || errno == EACCES) {
|
|
|
|
/* We may be trying to create a file where a directory
|
|
|
|
* used to be.
|
|
|
|
*/
|
|
|
|
struct stat st;
|
|
|
|
if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
|
|
|
|
errno = EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errno == EEXIST) {
|
|
|
|
unsigned int nr = getpid();
|
|
|
|
|
|
|
|
for (;;) {
|
2024-04-05 18:53:23 +08:00
|
|
|
newpath = mkpathdup("%s~%u", path, nr);
|
2018-08-14 00:14:38 +08:00
|
|
|
res = try_create_file(state, newpath, mode, buf, size);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res < 0)
|
2024-04-05 18:53:23 +08:00
|
|
|
goto out;
|
2016-04-23 02:55:46 +08:00
|
|
|
if (!res) {
|
|
|
|
if (!rename(newpath, path))
|
2024-04-05 18:53:23 +08:00
|
|
|
goto out;
|
2016-04-23 02:55:46 +08:00
|
|
|
unlink_or_warn(newpath);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (errno != EEXIST)
|
|
|
|
break;
|
|
|
|
++nr;
|
2024-04-05 18:53:23 +08:00
|
|
|
FREE_AND_NULL(newpath);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
}
|
2024-04-05 18:53:23 +08:00
|
|
|
res = error_errno(_("unable to write file '%s' mode %o"), path, mode);
|
|
|
|
out:
|
|
|
|
free(newpath);
|
|
|
|
return res;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int add_conflicted_stages_file(struct apply_state *state,
|
|
|
|
struct patch *patch)
|
|
|
|
{
|
|
|
|
int stage, namelen;
|
2018-07-03 03:49:31 +08:00
|
|
|
unsigned mode;
|
2016-04-23 02:55:46 +08:00
|
|
|
struct cache_entry *ce;
|
|
|
|
|
|
|
|
if (!state->update_index)
|
|
|
|
return 0;
|
|
|
|
namelen = strlen(patch->new_name);
|
|
|
|
mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
|
|
|
|
|
2018-08-14 00:14:40 +08:00
|
|
|
remove_file_from_index(state->repo->index, patch->new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
for (stage = 1; stage < 4; stage++) {
|
|
|
|
if (is_null_oid(&patch->threeway_stage[stage - 1]))
|
|
|
|
continue;
|
2018-08-14 00:14:40 +08:00
|
|
|
ce = make_empty_cache_entry(state->repo->index, namelen);
|
2016-04-23 02:55:46 +08:00
|
|
|
memcpy(ce->name, patch->new_name, namelen);
|
|
|
|
ce->ce_mode = create_ce_mode(mode);
|
|
|
|
ce->ce_flags = create_ce_flags(stage);
|
|
|
|
ce->ce_namelen = namelen;
|
2016-09-20 04:47:19 +08:00
|
|
|
oidcpy(&ce->oid, &patch->threeway_stage[stage - 1]);
|
2018-08-14 00:14:40 +08:00
|
|
|
if (add_index_entry(state->repo->index, ce, ADD_CACHE_OK_TO_ADD) < 0) {
|
2018-07-03 03:49:31 +08:00
|
|
|
discard_cache_entry(ce);
|
2016-04-23 02:55:46 +08:00
|
|
|
return error(_("unable to add cache entry for %s"),
|
|
|
|
patch->new_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_file(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
char *path = patch->new_name;
|
|
|
|
unsigned mode = patch->new_mode;
|
|
|
|
unsigned long size = patch->resultsize;
|
|
|
|
char *buf = patch->result;
|
|
|
|
|
|
|
|
if (!mode)
|
|
|
|
mode = S_IFREG | 0644;
|
|
|
|
if (create_one_file(state, path, mode, buf, size))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (patch->conflicted_threeway)
|
|
|
|
return add_conflicted_stages_file(state, patch);
|
2018-05-26 20:08:46 +08:00
|
|
|
else if (state->update_index)
|
2016-04-23 02:55:46 +08:00
|
|
|
return add_index_file(state, path, mode, buf, size);
|
2018-05-26 20:08:46 +08:00
|
|
|
return 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* phase zero is to remove, phase one is to create */
|
|
|
|
static int write_out_one_result(struct apply_state *state,
|
|
|
|
struct patch *patch,
|
|
|
|
int phase)
|
|
|
|
{
|
|
|
|
if (patch->is_delete > 0) {
|
|
|
|
if (phase == 0)
|
|
|
|
return remove_file(state, patch, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (patch->is_new > 0 || patch->is_copy) {
|
|
|
|
if (phase == 1)
|
|
|
|
return create_file(state, patch);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Rename or modification boils down to the same
|
|
|
|
* thing: remove the old, write the new
|
|
|
|
*/
|
|
|
|
if (phase == 0)
|
|
|
|
return remove_file(state, patch, patch->is_rename);
|
|
|
|
if (phase == 1)
|
|
|
|
return create_file(state, patch);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_out_one_reject(struct apply_state *state, struct patch *patch)
|
|
|
|
{
|
|
|
|
FILE *rej;
|
2024-04-17 04:16:05 +08:00
|
|
|
char *namebuf;
|
2016-04-23 02:55:46 +08:00
|
|
|
struct fragment *frag;
|
2023-03-09 23:02:54 +08:00
|
|
|
int fd, cnt = 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
|
|
|
for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
|
|
|
|
if (!frag->rejected)
|
|
|
|
continue;
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cnt) {
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_normal)
|
2016-04-23 02:55:46 +08:00
|
|
|
say_patch_name(stderr,
|
|
|
|
_("Applied patch %s cleanly."), patch);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This should not happen, because a removal patch that leaves
|
|
|
|
* contents are marked "rejected" at the patch level.
|
|
|
|
*/
|
|
|
|
if (!patch->new_name)
|
|
|
|
die(_("internal error"));
|
|
|
|
|
|
|
|
/* Say this even without --verbose */
|
|
|
|
strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...",
|
|
|
|
"Applying patch %%s with %d rejects...",
|
|
|
|
cnt),
|
|
|
|
cnt);
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
say_patch_name(stderr, sb.buf, patch);
|
2016-04-23 02:55:46 +08:00
|
|
|
strbuf_release(&sb);
|
|
|
|
|
2024-04-17 04:16:05 +08:00
|
|
|
namebuf = xstrfmt("%s.rej", patch->new_name);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2023-03-09 23:02:54 +08:00
|
|
|
fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
|
|
|
|
if (fd < 0) {
|
2024-04-17 04:16:05 +08:00
|
|
|
if (errno != EEXIST) {
|
|
|
|
error_errno(_("cannot open %s"), namebuf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (unlink(namebuf)) {
|
|
|
|
error_errno(_("cannot unlink '%s'"), namebuf);
|
|
|
|
goto error;
|
|
|
|
}
|
2023-03-09 23:02:54 +08:00
|
|
|
fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
|
2024-04-17 04:16:05 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
error_errno(_("cannot open %s"), namebuf);
|
|
|
|
goto error;
|
|
|
|
}
|
2023-03-09 23:02:54 +08:00
|
|
|
}
|
|
|
|
rej = fdopen(fd, "w");
|
2024-04-05 18:58:16 +08:00
|
|
|
if (!rej) {
|
|
|
|
error_errno(_("cannot open %s"), namebuf);
|
|
|
|
close(fd);
|
2024-04-17 04:16:05 +08:00
|
|
|
goto error;
|
2024-04-05 18:58:16 +08:00
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
/* Normal git tools never deal with .rej, so do not pretend
|
|
|
|
* this is a git patch by saying --git or giving extended
|
|
|
|
* headers. While at it, maybe please "kompare" that wants
|
|
|
|
* the trailing TAB and some garbage at the end of line ;-).
|
|
|
|
*/
|
|
|
|
fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
|
|
|
|
patch->new_name, patch->new_name);
|
|
|
|
for (cnt = 1, frag = patch->fragments;
|
|
|
|
frag;
|
|
|
|
cnt++, frag = frag->next) {
|
|
|
|
if (!frag->rejected) {
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
|
2016-04-23 02:55:46 +08:00
|
|
|
continue;
|
|
|
|
}
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent)
|
|
|
|
fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
|
2016-04-23 02:55:46 +08:00
|
|
|
fprintf(rej, "%.*s", frag->size, frag->patch);
|
|
|
|
if (frag->patch[frag->size-1] != '\n')
|
|
|
|
fputc('\n', rej);
|
|
|
|
}
|
|
|
|
fclose(rej);
|
2024-04-17 04:16:05 +08:00
|
|
|
error:
|
|
|
|
free(namebuf);
|
2016-04-23 02:55:46 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns:
|
|
|
|
* -1 if an error happened
|
|
|
|
* 0 if the patch applied cleanly
|
|
|
|
* 1 if the patch did not apply cleanly
|
|
|
|
*/
|
|
|
|
static int write_out_results(struct apply_state *state, struct patch *list)
|
|
|
|
{
|
|
|
|
int phase;
|
|
|
|
int errs = 0;
|
|
|
|
struct patch *l;
|
|
|
|
struct string_list cpath = STRING_LIST_INIT_DUP;
|
|
|
|
|
|
|
|
for (phase = 0; phase < 2; phase++) {
|
|
|
|
l = list;
|
|
|
|
while (l) {
|
|
|
|
if (l->rejected)
|
|
|
|
errs = 1;
|
|
|
|
else {
|
|
|
|
if (write_out_one_result(state, l, phase)) {
|
|
|
|
string_list_clear(&cpath, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (phase == 1) {
|
|
|
|
if (write_out_one_reject(state, l))
|
|
|
|
errs = 1;
|
|
|
|
if (l->conflicted_threeway) {
|
|
|
|
string_list_append(&cpath, l->new_name);
|
|
|
|
errs = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpath.nr) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
|
|
|
|
string_list_sort(&cpath);
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_silent) {
|
|
|
|
for_each_string_list_item(item, &cpath)
|
|
|
|
fprintf(stderr, "U %s\n", item->string);
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
string_list_clear(&cpath, 0);
|
|
|
|
|
2021-04-08 10:13:44 +08:00
|
|
|
/*
|
|
|
|
* rerere relies on the partially merged result being in the working
|
|
|
|
* tree with conflict markers, but that isn't written with --cached.
|
|
|
|
*/
|
|
|
|
if (!state->cached)
|
|
|
|
repo_rerere(state->repo, 0);
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return errs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to apply a patch.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* -128 if a bad error happened (like patch unreadable)
|
|
|
|
* -1 if patch did not apply and user cannot deal with it
|
|
|
|
* 0 if the patch applied
|
|
|
|
* 1 if the patch did not apply but user might fix it
|
|
|
|
*/
|
|
|
|
static int apply_patch(struct apply_state *state,
|
|
|
|
int fd,
|
|
|
|
const char *filename,
|
|
|
|
int options)
|
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
struct strbuf buf = STRBUF_INIT; /* owns the patch text */
|
|
|
|
struct patch *list = NULL, **listp = &list;
|
|
|
|
int skipped_patch = 0;
|
|
|
|
int res = 0;
|
2019-09-03 06:39:44 +08:00
|
|
|
int flush_attributes = 0;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
state->patch_input_file = filename;
|
|
|
|
if (read_patch_file(&buf, fd) < 0)
|
|
|
|
return -128;
|
|
|
|
offset = 0;
|
|
|
|
while (offset < buf.len) {
|
|
|
|
struct patch *patch;
|
|
|
|
int nr;
|
|
|
|
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(patch, 1);
|
2016-04-23 02:55:46 +08:00
|
|
|
patch->inaccurate_eof = !!(options & APPLY_OPT_INACCURATE_EOF);
|
|
|
|
patch->recount = !!(options & APPLY_OPT_RECOUNT);
|
|
|
|
nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch);
|
|
|
|
if (nr < 0) {
|
|
|
|
free_patch(patch);
|
|
|
|
if (nr == -128) {
|
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (state->apply_in_reverse)
|
|
|
|
reverse_patches(patch);
|
|
|
|
if (use_patch(state, patch)) {
|
|
|
|
patch_stats(state, patch);
|
apply: when -R, also reverse list of sections
A patch changing a symlink into a file is written with 2 sections (in
the code, represented as "struct patch"): firstly, the deletion of the
symlink, and secondly, the creation of the file. When applying that
patch with -R, the sections are reversed, so we get:
(1) creation of a symlink, then
(2) deletion of a file.
This causes an issue when the "deletion of a file" section is checked,
because Git observes that the so-called file is not a file but a
symlink, resulting in a "wrong type" error message.
What we want is:
(1) deletion of a file, then
(2) creation of a symlink.
In the code, this is reflected in the behavior of previous_patch() when
invoked from check_preimage() when the deletion is checked. Creation
then deletion means that when the deletion is checked, previous_patch()
returns the creation section, triggering a mode conflict resulting in
the "wrong type" error message. But deletion then creation means that
when the deletion is checked, previous_patch() returns NULL, so the
deletion mode is checked against lstat, which is what we want.
There are also other ways a patch can contain 2 sections referencing the
same file, for example, in 7a07841c0b ("git-apply: handle a patch that
touches the same path more than once better", 2008-06-27). "git apply
-R" fails in the same way, and this commit makes this case succeed.
Therefore, when building the list of sections, build them in reverse
order (by adding to the front of the list instead of the back) when -R
is passed.
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-10-21 06:04:52 +08:00
|
|
|
if (!list || !state->apply_in_reverse) {
|
|
|
|
*listp = patch;
|
|
|
|
listp = &patch->next;
|
|
|
|
} else {
|
|
|
|
patch->next = list;
|
|
|
|
list = patch;
|
|
|
|
}
|
2019-09-03 06:39:44 +08:00
|
|
|
|
|
|
|
if ((patch->new_name &&
|
|
|
|
ends_with_path_components(patch->new_name,
|
|
|
|
GITATTRIBUTES_FILE)) ||
|
|
|
|
(patch->old_name &&
|
|
|
|
ends_with_path_components(patch->old_name,
|
|
|
|
GITATTRIBUTES_FILE)))
|
|
|
|
flush_attributes = 1;
|
2016-04-23 02:55:46 +08:00
|
|
|
}
|
|
|
|
else {
|
2016-09-05 04:18:25 +08:00
|
|
|
if (state->apply_verbosity > verbosity_normal)
|
2016-04-23 02:55:46 +08:00
|
|
|
say_patch_name(stderr, _("Skipped patch '%s'."), patch);
|
|
|
|
free_patch(patch);
|
|
|
|
skipped_patch++;
|
|
|
|
}
|
|
|
|
offset += nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!list && !skipped_patch) {
|
2021-12-14 06:03:27 +08:00
|
|
|
if (!state->allow_empty) {
|
|
|
|
error(_("No valid patches in input (allow with \"--allow-empty\")"));
|
|
|
|
res = -128;
|
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
|
|
|
|
state->apply = 0;
|
|
|
|
|
2018-05-26 20:08:46 +08:00
|
|
|
state->update_index = (state->check_index || state->ita_only) && state->apply;
|
2017-10-06 04:32:10 +08:00
|
|
|
if (state->update_index && !is_lock_file_locked(&state->lock_file)) {
|
2016-09-05 04:18:32 +08:00
|
|
|
if (state->index_file)
|
2017-10-06 04:32:10 +08:00
|
|
|
hold_lock_file_for_update(&state->lock_file,
|
|
|
|
state->index_file,
|
|
|
|
LOCK_DIE_ON_ERROR);
|
2016-09-05 04:18:32 +08:00
|
|
|
else
|
2019-01-12 10:13:24 +08:00
|
|
|
repo_hold_locked_index(state->repo, &state->lock_file,
|
|
|
|
LOCK_DIE_ON_ERROR);
|
2016-09-05 04:18:32 +08:00
|
|
|
}
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2016-09-05 04:18:32 +08:00
|
|
|
if (state->check_index && read_apply_cache(state) < 0) {
|
2016-04-23 02:55:46 +08:00
|
|
|
error(_("unable to read index file"));
|
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->check || state->apply) {
|
|
|
|
int r = check_patch_list(state, list);
|
|
|
|
if (r == -128) {
|
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (r < 0 && !state->apply_with_reject) {
|
|
|
|
res = -1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->apply) {
|
|
|
|
int write_res = write_out_results(state, list);
|
|
|
|
if (write_res < 0) {
|
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (write_res > 0) {
|
|
|
|
/* with --3way, we still need to write the index out */
|
|
|
|
res = state->apply_with_reject ? -1 : 1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->fake_ancestor &&
|
2016-09-05 04:18:31 +08:00
|
|
|
build_fake_ancestor(state, list)) {
|
2016-04-23 02:55:46 +08:00
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:26 +08:00
|
|
|
if (state->diffstat && state->apply_verbosity > verbosity_silent)
|
2016-04-23 02:55:46 +08:00
|
|
|
stat_patch_list(state, list);
|
|
|
|
|
2016-09-05 04:18:26 +08:00
|
|
|
if (state->numstat && state->apply_verbosity > verbosity_silent)
|
2016-04-23 02:55:46 +08:00
|
|
|
numstat_patch_list(state, list);
|
|
|
|
|
2016-09-05 04:18:26 +08:00
|
|
|
if (state->summary && state->apply_verbosity > verbosity_silent)
|
2016-04-23 02:55:46 +08:00
|
|
|
summary_patch_list(list);
|
|
|
|
|
2019-09-03 06:39:44 +08:00
|
|
|
if (flush_attributes)
|
|
|
|
reset_parsed_attributes();
|
2016-04-23 02:55:46 +08:00
|
|
|
end:
|
|
|
|
free_patch_list(list);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
string_list_clear(&state->fn_table, 0);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:30 +08:00
|
|
|
static int apply_option_parse_exclude(const struct option *opt,
|
|
|
|
const char *arg, int unset)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct apply_state *state = opt->value;
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 14:45:42 +08:00
|
|
|
|
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
add_name_limit(state, arg, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:30 +08:00
|
|
|
static int apply_option_parse_include(const struct option *opt,
|
|
|
|
const char *arg, int unset)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct apply_state *state = opt->value;
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 14:45:42 +08:00
|
|
|
|
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
add_name_limit(state, arg, 0);
|
|
|
|
state->has_include = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:30 +08:00
|
|
|
static int apply_option_parse_p(const struct option *opt,
|
|
|
|
const char *arg,
|
|
|
|
int unset)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct apply_state *state = opt->value;
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 14:45:42 +08:00
|
|
|
|
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
state->p_value = atoi(arg);
|
|
|
|
state->p_value_known = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:30 +08:00
|
|
|
static int apply_option_parse_space_change(const struct option *opt,
|
|
|
|
const char *arg, int unset)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct apply_state *state = opt->value;
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 14:45:42 +08:00
|
|
|
|
|
|
|
BUG_ON_OPT_ARG(arg);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
if (unset)
|
|
|
|
state->ws_ignore_action = ignore_ws_none;
|
|
|
|
else
|
|
|
|
state->ws_ignore_action = ignore_ws_change;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:30 +08:00
|
|
|
static int apply_option_parse_whitespace(const struct option *opt,
|
|
|
|
const char *arg, int unset)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct apply_state *state = opt->value;
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 14:45:42 +08:00
|
|
|
|
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
state->whitespace_option = arg;
|
|
|
|
if (parse_whitespace_option(state, arg))
|
2018-11-05 14:43:59 +08:00
|
|
|
return -1;
|
2016-04-23 02:55:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:30 +08:00
|
|
|
static int apply_option_parse_directory(const struct option *opt,
|
|
|
|
const char *arg, int unset)
|
2016-04-23 02:55:46 +08:00
|
|
|
{
|
|
|
|
struct apply_state *state = opt->value;
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 14:45:42 +08:00
|
|
|
|
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
|
2016-04-23 02:55:46 +08:00
|
|
|
strbuf_reset(&state->root);
|
|
|
|
strbuf_addstr(&state->root, arg);
|
|
|
|
strbuf_complete(&state->root, '/');
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int apply_all_patches(struct apply_state *state,
|
|
|
|
int argc,
|
|
|
|
const char **argv,
|
|
|
|
int options)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int res;
|
|
|
|
int errs = 0;
|
|
|
|
int read_stdin = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
const char *arg = argv[i];
|
2017-03-21 09:28:49 +08:00
|
|
|
char *to_free = NULL;
|
2016-04-23 02:55:46 +08:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (!strcmp(arg, "-")) {
|
|
|
|
res = apply_patch(state, 0, "<stdin>", options);
|
|
|
|
if (res < 0)
|
|
|
|
goto end;
|
|
|
|
errs |= res;
|
|
|
|
read_stdin = 0;
|
|
|
|
continue;
|
2017-03-21 09:28:49 +08:00
|
|
|
} else
|
|
|
|
arg = to_free = prefix_filename(state->prefix, arg);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
fd = open(arg, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
error(_("can't open patch '%s': %s"), arg, strerror(errno));
|
|
|
|
res = -128;
|
2017-03-21 09:28:49 +08:00
|
|
|
free(to_free);
|
2016-04-23 02:55:46 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
read_stdin = 0;
|
|
|
|
set_default_whitespace_mode(state);
|
|
|
|
res = apply_patch(state, fd, arg, options);
|
|
|
|
close(fd);
|
2017-03-21 09:28:49 +08:00
|
|
|
free(to_free);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res < 0)
|
|
|
|
goto end;
|
|
|
|
errs |= res;
|
|
|
|
}
|
|
|
|
set_default_whitespace_mode(state);
|
|
|
|
if (read_stdin) {
|
|
|
|
res = apply_patch(state, 0, "<stdin>", options);
|
|
|
|
if (res < 0)
|
|
|
|
goto end;
|
|
|
|
errs |= res;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->whitespace_error) {
|
|
|
|
if (state->squelch_whitespace_errors &&
|
|
|
|
state->squelch_whitespace_errors < state->whitespace_error) {
|
|
|
|
int squelched =
|
|
|
|
state->whitespace_error - state->squelch_whitespace_errors;
|
|
|
|
warning(Q_("squelched %d whitespace error",
|
|
|
|
"squelched %d whitespace errors",
|
|
|
|
squelched),
|
|
|
|
squelched);
|
|
|
|
}
|
|
|
|
if (state->ws_error_action == die_on_ws_error) {
|
|
|
|
error(Q_("%d line adds whitespace errors.",
|
|
|
|
"%d lines add whitespace errors.",
|
|
|
|
state->whitespace_error),
|
|
|
|
state->whitespace_error);
|
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (state->applied_after_fixing_ws && state->apply)
|
2016-10-14 19:43:35 +08:00
|
|
|
warning(Q_("%d line applied after"
|
|
|
|
" fixing whitespace errors.",
|
|
|
|
"%d lines applied after"
|
|
|
|
" fixing whitespace errors.",
|
|
|
|
state->applied_after_fixing_ws),
|
|
|
|
state->applied_after_fixing_ws);
|
2016-04-23 02:55:46 +08:00
|
|
|
else if (state->whitespace_error)
|
|
|
|
warning(Q_("%d line adds whitespace errors.",
|
|
|
|
"%d lines add whitespace errors.",
|
|
|
|
state->whitespace_error),
|
|
|
|
state->whitespace_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->update_index) {
|
2018-08-14 00:14:40 +08:00
|
|
|
res = write_locked_index(state->repo->index, &state->lock_file, COMMIT_LOCK);
|
2016-04-23 02:55:46 +08:00
|
|
|
if (res) {
|
|
|
|
error(_("Unable to write new index file"));
|
|
|
|
res = -128;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:18:29 +08:00
|
|
|
res = !!errs;
|
2016-04-23 02:55:46 +08:00
|
|
|
|
|
|
|
end:
|
2017-10-06 04:32:10 +08:00
|
|
|
rollback_lock_file(&state->lock_file);
|
2016-04-23 02:55:46 +08:00
|
|
|
|
2016-09-05 04:18:29 +08:00
|
|
|
if (state->apply_verbosity <= verbosity_silent) {
|
|
|
|
set_error_routine(state->saved_error_routine);
|
|
|
|
set_warn_routine(state->saved_warn_routine);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res > -1)
|
|
|
|
return res;
|
2016-04-23 02:55:46 +08:00
|
|
|
return (res == -1 ? 1 : 128);
|
|
|
|
}
|
2016-09-05 04:18:30 +08:00
|
|
|
|
|
|
|
int apply_parse_options(int argc, const char **argv,
|
|
|
|
struct apply_state *state,
|
|
|
|
int *force_apply, int *options,
|
|
|
|
const char * const *apply_usage)
|
|
|
|
{
|
|
|
|
struct option builtin_apply_options[] = {
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
OPT_CALLBACK_F(0, "exclude", state, N_("path"),
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("don't apply changes matching the given path"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
PARSE_OPT_NONEG, apply_option_parse_exclude),
|
|
|
|
OPT_CALLBACK_F(0, "include", state, N_("path"),
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("apply changes matching the given path"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
PARSE_OPT_NONEG, apply_option_parse_include),
|
|
|
|
OPT_CALLBACK('p', NULL, state, N_("num"),
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("remove <num> leading slashes from traditional diff paths"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
apply_option_parse_p),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_BOOL(0, "no-add", &state->no_add,
|
|
|
|
N_("ignore additions made by the patch")),
|
|
|
|
OPT_BOOL(0, "stat", &state->diffstat,
|
|
|
|
N_("instead of applying the patch, output diffstat for the input")),
|
|
|
|
OPT_NOOP_NOARG(0, "allow-binary-replacement"),
|
|
|
|
OPT_NOOP_NOARG(0, "binary"),
|
|
|
|
OPT_BOOL(0, "numstat", &state->numstat,
|
|
|
|
N_("show number of added and deleted lines in decimal notation")),
|
|
|
|
OPT_BOOL(0, "summary", &state->summary,
|
|
|
|
N_("instead of applying the patch, output a summary for the input")),
|
|
|
|
OPT_BOOL(0, "check", &state->check,
|
|
|
|
N_("instead of applying the patch, see if the patch is applicable")),
|
|
|
|
OPT_BOOL(0, "index", &state->check_index,
|
|
|
|
N_("make sure the patch is applicable to the current index")),
|
2018-05-26 20:08:46 +08:00
|
|
|
OPT_BOOL('N', "intent-to-add", &state->ita_only,
|
|
|
|
N_("mark new files with `git add --intent-to-add`")),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_BOOL(0, "cached", &state->cached,
|
|
|
|
N_("apply a patch without touching the working tree")),
|
2018-02-09 19:01:46 +08:00
|
|
|
OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
|
|
|
|
N_("accept a patch that touches outside the working area"),
|
|
|
|
PARSE_OPT_NOCOMPLETE),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_BOOL(0, "apply", force_apply,
|
|
|
|
N_("also apply the patch (use with --stat/--summary/--check)")),
|
|
|
|
OPT_BOOL('3', "3way", &state->threeway,
|
2021-04-07 07:25:32 +08:00
|
|
|
N_( "attempt three-way merge, fall back on normal patch if that fails")),
|
2024-09-09 22:10:58 +08:00
|
|
|
OPT_SET_INT_F(0, "ours", &state->merge_variant,
|
|
|
|
N_("for conflicts, use our version"),
|
|
|
|
XDL_MERGE_FAVOR_OURS, PARSE_OPT_NONEG),
|
|
|
|
OPT_SET_INT_F(0, "theirs", &state->merge_variant,
|
|
|
|
N_("for conflicts, use their version"),
|
|
|
|
XDL_MERGE_FAVOR_THEIRS, PARSE_OPT_NONEG),
|
|
|
|
OPT_SET_INT_F(0, "union", &state->merge_variant,
|
|
|
|
N_("for conflicts, use a union version"),
|
|
|
|
XDL_MERGE_FAVOR_UNION, PARSE_OPT_NONEG),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor,
|
|
|
|
N_("build a temporary index based on embedded index information")),
|
|
|
|
/* Think twice before adding "--nul" synonym to this */
|
|
|
|
OPT_SET_INT('z', NULL, &state->line_termination,
|
|
|
|
N_("paths are separated with NUL character"), '\0'),
|
|
|
|
OPT_INTEGER('C', NULL, &state->p_context,
|
|
|
|
N_("ensure at least <n> lines of context match")),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
OPT_CALLBACK(0, "whitespace", state, N_("action"),
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("detect new or modified lines that have whitespace errors"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
apply_option_parse_whitespace),
|
|
|
|
OPT_CALLBACK_F(0, "ignore-space-change", state, NULL,
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("ignore changes in whitespace when finding context"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
PARSE_OPT_NOARG, apply_option_parse_space_change),
|
|
|
|
OPT_CALLBACK_F(0, "ignore-whitespace", state, NULL,
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("ignore changes in whitespace when finding context"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
PARSE_OPT_NOARG, apply_option_parse_space_change),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_BOOL('R', "reverse", &state->apply_in_reverse,
|
|
|
|
N_("apply the patch in reverse")),
|
|
|
|
OPT_BOOL(0, "unidiff-zero", &state->unidiff_zero,
|
|
|
|
N_("don't expect at least one line of context")),
|
|
|
|
OPT_BOOL(0, "reject", &state->apply_with_reject,
|
|
|
|
N_("leave the rejected hunks in corresponding *.rej files")),
|
|
|
|
OPT_BOOL(0, "allow-overlap", &state->allow_overlap,
|
|
|
|
N_("allow overlapping hunks")),
|
2021-12-14 06:03:26 +08:00
|
|
|
OPT__VERBOSITY(&state->apply_verbosity),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_BIT(0, "inaccurate-eof", options,
|
|
|
|
N_("tolerate incorrectly detected missing new-line at the end of file"),
|
|
|
|
APPLY_OPT_INACCURATE_EOF),
|
|
|
|
OPT_BIT(0, "recount", options,
|
|
|
|
N_("do not trust the line counts in the hunk headers"),
|
|
|
|
APPLY_OPT_RECOUNT),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
OPT_CALLBACK(0, "directory", state, N_("root"),
|
2016-09-05 04:18:30 +08:00
|
|
|
N_("prepend <root> to all filenames"),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
apply_option_parse_directory),
|
2021-12-14 06:03:27 +08:00
|
|
|
OPT_BOOL(0, "allow-empty", &state->allow_empty,
|
|
|
|
N_("don't return error for empty patches")),
|
2016-09-05 04:18:30 +08:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2024-09-09 22:10:58 +08:00
|
|
|
argc = parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0);
|
|
|
|
|
|
|
|
if (state->merge_variant && !state->threeway)
|
|
|
|
die(_("--ours, --theirs, and --union require --3way"));
|
|
|
|
|
|
|
|
return argc;
|
2016-09-05 04:18:30 +08:00
|
|
|
}
|