diff options
Diffstat (limited to 'convert.c')
-rw-r--r-- | convert.c | 265 |
1 files changed, 139 insertions, 126 deletions
@@ -1,14 +1,21 @@ -#include "cache.h" +#include "git-compat-util.h" +#include "advice.h" #include "config.h" -#include "object-store.h" +#include "convert.h" +#include "copy.h" +#include "gettext.h" +#include "hex.h" +#include "object-store-ll.h" #include "attr.h" #include "run-command.h" #include "quote.h" +#include "read-cache-ll.h" #include "sigchain.h" #include "pkt-line.h" #include "sub-process.h" +#include "trace.h" #include "utf8.h" -#include "ll-merge.h" +#include "merge-ll.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -24,17 +31,6 @@ #define CONVERT_STAT_BITS_TXT_CRLF 0x2 #define CONVERT_STAT_BITS_BIN 0x4 -enum crlf_action { - CRLF_UNDEFINED, - CRLF_BINARY, - CRLF_TEXT, - CRLF_TEXT_INPUT, - CRLF_TEXT_CRLF, - CRLF_AUTO, - CRLF_AUTO_INPUT, - CRLF_AUTO_CRLF -}; - struct text_stat { /* NUL, CR, LF and CRLF counts */ unsigned nul, lonecr, lonelf, crlf; @@ -138,7 +134,7 @@ static const char *gather_convert_stats_ascii(const char *data, unsigned long si } } -const char *get_cached_convert_stats_ascii(const struct index_state *istate, +const char *get_cached_convert_stats_ascii(struct index_state *istate, const char *path) { const char *ret; @@ -172,7 +168,7 @@ static int text_eol_is_crlf(void) return 0; } -static enum eol output_eol(enum crlf_action crlf_action) +static enum eol output_eol(enum convert_crlf_action crlf_action) { switch (crlf_action) { case CRLF_BINARY: @@ -195,7 +191,7 @@ static enum eol output_eol(enum crlf_action crlf_action) return core_eol; } -static void check_global_conv_flags_eol(const char *path, enum crlf_action crlf_action, +static void check_global_conv_flags_eol(const char *path, struct text_stat *old_stats, struct text_stat *new_stats, int conv_flags) { @@ -206,9 +202,9 @@ static void check_global_conv_flags_eol(const char *path, enum crlf_action crlf_ if (conv_flags & CONV_EOL_RNDTRP_DIE) die(_("CRLF would be replaced by LF in %s"), path); else if (conv_flags & CONV_EOL_RNDTRP_WARN) - warning(_("CRLF will be replaced by LF in %s.\n" - "The file will have its original line" - " endings in your working directory"), path); + warning(_("in the working copy of '%s', CRLF will be" + " replaced by LF the next time Git touches" + " it"), path); } else if (old_stats->lonelf && !new_stats->lonelf ) { /* * CRLFs would be added by checkout @@ -216,13 +212,13 @@ static void check_global_conv_flags_eol(const char *path, enum crlf_action crlf_ if (conv_flags & CONV_EOL_RNDTRP_DIE) die(_("LF would be replaced by CRLF in %s"), path); else if (conv_flags & CONV_EOL_RNDTRP_WARN) - warning(_("LF will be replaced by CRLF in %s.\n" - "The file will have its original line" - " endings in your working directory"), path); + warning(_("in the working copy of '%s', LF will be" + " replaced by CRLF the next time Git touches" + " it"), path); } } -static int has_crlf_in_index(const struct index_state *istate, const char *path) +static int has_crlf_in_index(struct index_state *istate, const char *path) { unsigned long sz; void *data; @@ -246,7 +242,7 @@ static int has_crlf_in_index(const struct index_state *istate, const char *path) } static int will_convert_lf_to_crlf(struct text_stat *stats, - enum crlf_action crlf_action) + enum convert_crlf_action crlf_action) { if (output_eol(crlf_action) != EOL_CRLF) return 0; @@ -496,10 +492,10 @@ static int encode_to_worktree(const char *path, const char *src, size_t src_len, return 1; } -static int crlf_to_git(const struct index_state *istate, +static int crlf_to_git(struct index_state *istate, const char *path, const char *src, size_t len, struct strbuf *buf, - enum crlf_action crlf_action, int conv_flags) + enum convert_crlf_action crlf_action, int conv_flags) { struct text_stat stats; char *dst; @@ -547,7 +543,7 @@ static int crlf_to_git(const struct index_state *istate, new_stats.crlf += new_stats.lonelf; new_stats.lonelf = 0; } - check_global_conv_flags_eol(path, crlf_action, &stats, &new_stats, conv_flags); + check_global_conv_flags_eol(path, &stats, &new_stats, conv_flags); } if (!convert_crlf_into_lf) return 0; @@ -585,8 +581,8 @@ static int crlf_to_git(const struct index_state *istate, return 1; } -static int crlf_to_worktree(const char *src, size_t len, - struct strbuf *buf, enum crlf_action crlf_action) +static int crlf_to_worktree(const char *src, size_t len, struct strbuf *buf, + enum convert_crlf_action crlf_action) { char *to_free = NULL; struct text_stat stats; @@ -624,41 +620,36 @@ static int crlf_to_worktree(const char *src, size_t len, struct filter_params { const char *src; - unsigned long size; + size_t size; int fd; const char *cmd; const char *path; }; -static int filter_buffer_or_fd(int in, int out, void *data) +static int filter_buffer_or_fd(int in UNUSED, int out, void *data) { /* * Spawn cmd and feed the buffer contents through its stdin. */ struct child_process child_process = CHILD_PROCESS_INIT; struct filter_params *params = (struct filter_params *)data; + const char *format = params->cmd; int write_err, status; - const char *argv[] = { NULL, NULL }; /* apply % substitution to cmd */ struct strbuf cmd = STRBUF_INIT; - struct strbuf path = STRBUF_INIT; - struct strbuf_expand_dict_entry dict[] = { - { "f", NULL, }, - { NULL, NULL, }, - }; - /* quote the path to preserve spaces, etc. */ - sq_quote_buf(&path, params->path); - dict[0].value = path.buf; - - /* expand all %f with the quoted path */ - strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict); - strbuf_release(&path); - - argv[0] = cmd.buf; + /* expand all %f with the quoted path; quote to preserve space, etc. */ + while (strbuf_expand_step(&cmd, &format)) { + if (skip_prefix(format, "%", &format)) + strbuf_addch(&cmd, '%'); + else if (skip_prefix(format, "f", &format)) + sq_quote_buf(&cmd, params->path); + else + strbuf_addch(&cmd, '%'); + } - child_process.argv = argv; + strvec_push(&child_process.args, cmd.buf); child_process.use_shell = 1; child_process.in = -1; child_process.out = out; @@ -887,9 +878,13 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len goto done; if (fd >= 0) - err = write_packetized_from_fd(fd, process->in); + err = write_packetized_from_fd_no_flush(fd, process->in); else - err = write_packetized_from_buf(src, len, process->in); + err = write_packetized_from_buf_no_flush(src, len, process->in); + if (err) + goto done; + + err = packet_flush_gently(process->in); if (err) goto done; @@ -906,7 +901,8 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (err) goto done; - err = read_packetized_to_strbuf(process->out, &nbuf) < 0; + err = read_packetized_to_strbuf(process->out, &nbuf, + PACKET_READ_GENTLE_ON_EOF) < 0; if (err) goto done; @@ -925,6 +921,7 @@ done: else strbuf_swap(dst, &nbuf); strbuf_release(&nbuf); + strbuf_release(&filter_status); return !err; } @@ -975,6 +972,7 @@ done: if (err) handle_filter_error(&filter_status, entry, 0); + strbuf_release(&filter_status); return !err; } @@ -1015,7 +1013,9 @@ static int apply_filter(const char *path, const char *src, size_t len, return 0; } -static int read_convert_config(const char *var, const char *value, void *cb) +static int read_convert_config(const char *var, const char *value, + const struct config_context *ctx UNUSED, + void *cb UNUSED) { const char *key, *name; size_t namelen; @@ -1028,10 +1028,10 @@ static int read_convert_config(const char *var, const char *value, void *cb) if (parse_config_key(var, "filter", &name, &namelen, &key) < 0 || !name) return 0; for (drv = user_convert; drv; drv = drv->next) - if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) + if (!xstrncmpz(drv->name, name, namelen)) break; if (!drv) { - drv = xcalloc(1, sizeof(struct convert_driver)); + CALLOC_ARRAY(drv, 1); drv->name = xmemdupz(name, namelen); *user_convert_tail = drv; user_convert_tail = &(drv->next); @@ -1166,7 +1166,7 @@ static int ident_to_worktree(const char *src, size_t len, /* are we "faking" in place editing ? */ if (src == buf->buf) to_free = strbuf_detach(buf, NULL); - hash_object_file(the_hash_algo, src, len, "blob", &oid); + hash_object_file(the_hash_algo, src, len, OBJ_BLOB, &oid); strbuf_grow(buf, len + cnt * (the_hash_algo->hexsz + 3)); for (;;) { @@ -1250,7 +1250,7 @@ static const char *git_path_check_encoding(struct attr_check_item *check) return value; } -static enum crlf_action git_path_check_crlf(struct attr_check_item *check) +static enum convert_crlf_action git_path_check_crlf(struct attr_check_item *check) { const char *value = check->value; @@ -1300,18 +1300,10 @@ static int git_path_check_ident(struct attr_check_item *check) return !!ATTR_TRUE(value); } -struct conv_attrs { - struct convert_driver *drv; - enum crlf_action attr_action; /* What attr says */ - enum crlf_action crlf_action; /* When no attr is set, use core.autocrlf */ - int ident; - const char *working_tree_encoding; /* Supported encoding or default encoding if NULL */ -}; - static struct attr_check *check; -static void convert_attrs(const struct index_state *istate, - struct conv_attrs *ca, const char *path) +void convert_attrs(struct index_state *istate, + struct conv_attrs *ca, const char *path) { struct attr_check_item *ccheck = NULL; @@ -1372,7 +1364,7 @@ void reset_parsed_attributes(void) user_convert_tail = NULL; } -int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path) +int would_convert_to_git_filter_fd(struct index_state *istate, const char *path) { struct conv_attrs ca; @@ -1391,7 +1383,7 @@ int would_convert_to_git_filter_fd(const struct index_state *istate, const char return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL, NULL); } -const char *get_convert_attr_ascii(const struct index_state *istate, const char *path) +const char *get_convert_attr_ascii(struct index_state *istate, const char *path) { struct conv_attrs ca; @@ -1417,7 +1409,7 @@ const char *get_convert_attr_ascii(const struct index_state *istate, const char return ""; } -int convert_to_git(const struct index_state *istate, +int convert_to_git(struct index_state *istate, const char *path, const char *src, size_t len, struct strbuf *dst, int conv_flags) { @@ -1451,7 +1443,7 @@ int convert_to_git(const struct index_state *istate, return ret | ident_to_git(src, len, dst, ca.ident); } -void convert_to_git_filter_fd(const struct index_state *istate, +void convert_to_git_filter_fd(struct index_state *istate, const char *path, int fd, struct strbuf *dst, int conv_flags) { @@ -1459,7 +1451,6 @@ void convert_to_git_filter_fd(const struct index_state *istate, convert_attrs(istate, &ca, path); assert(ca.drv); - assert(ca.drv->clean || ca.drv->process); if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL, NULL)) die(_("%s: clean filter '%s' failed"), path, ca.drv->name); @@ -1469,19 +1460,16 @@ void convert_to_git_filter_fd(const struct index_state *istate, ident_to_git(dst->buf, dst->len, dst, ca.ident); } -static int convert_to_working_tree_internal(const struct index_state *istate, - const char *path, const char *src, - size_t len, struct strbuf *dst, - int normalizing, - const struct checkout_metadata *meta, - struct delayed_checkout *dco) +static int convert_to_working_tree_ca_internal(const struct conv_attrs *ca, + const char *path, const char *src, + size_t len, struct strbuf *dst, + int normalizing, + const struct checkout_metadata *meta, + struct delayed_checkout *dco) { int ret = 0, ret_filter = 0; - struct conv_attrs ca; - - convert_attrs(istate, &ca, path); - ret |= ident_to_worktree(src, len, dst, ca.ident); + ret |= ident_to_worktree(src, len, dst, ca->ident); if (ret) { src = dst->buf; len = dst->len; @@ -1491,49 +1479,56 @@ static int convert_to_working_tree_internal(const struct index_state *istate, * is a smudge or process filter (even if the process filter doesn't * support smudge). The filters might expect CRLFs. */ - if ((ca.drv && (ca.drv->smudge || ca.drv->process)) || !normalizing) { - ret |= crlf_to_worktree(src, len, dst, ca.crlf_action); + if ((ca->drv && (ca->drv->smudge || ca->drv->process)) || !normalizing) { + ret |= crlf_to_worktree(src, len, dst, ca->crlf_action); if (ret) { src = dst->buf; len = dst->len; } } - ret |= encode_to_worktree(path, src, len, dst, ca.working_tree_encoding); + ret |= encode_to_worktree(path, src, len, dst, ca->working_tree_encoding); if (ret) { src = dst->buf; len = dst->len; } ret_filter = apply_filter( - path, src, len, -1, dst, ca.drv, CAP_SMUDGE, meta, dco); - if (!ret_filter && ca.drv && ca.drv->required) - die(_("%s: smudge filter %s failed"), path, ca.drv->name); + path, src, len, -1, dst, ca->drv, CAP_SMUDGE, meta, dco); + if (!ret_filter && ca->drv && ca->drv->required) + die(_("%s: smudge filter %s failed"), path, ca->drv->name); return ret | ret_filter; } -int async_convert_to_working_tree(const struct index_state *istate, - const char *path, const char *src, - size_t len, struct strbuf *dst, - const struct checkout_metadata *meta, - void *dco) +int async_convert_to_working_tree_ca(const struct conv_attrs *ca, + const char *path, const char *src, + size_t len, struct strbuf *dst, + const struct checkout_metadata *meta, + void *dco) { - return convert_to_working_tree_internal(istate, path, src, len, dst, 0, meta, dco); + return convert_to_working_tree_ca_internal(ca, path, src, len, dst, 0, + meta, dco); } -int convert_to_working_tree(const struct index_state *istate, - const char *path, const char *src, - size_t len, struct strbuf *dst, - const struct checkout_metadata *meta) +int convert_to_working_tree_ca(const struct conv_attrs *ca, + const char *path, const char *src, + size_t len, struct strbuf *dst, + const struct checkout_metadata *meta) { - return convert_to_working_tree_internal(istate, path, src, len, dst, 0, meta, NULL); + return convert_to_working_tree_ca_internal(ca, path, src, len, dst, 0, + meta, NULL); } -int renormalize_buffer(const struct index_state *istate, const char *path, +int renormalize_buffer(struct index_state *istate, const char *path, const char *src, size_t len, struct strbuf *dst) { - int ret = convert_to_working_tree_internal(istate, path, src, len, dst, 1, NULL, NULL); + struct conv_attrs ca; + int ret; + + convert_attrs(istate, &ca, path); + ret = convert_to_working_tree_ca_internal(&ca, path, src, len, dst, 1, + NULL, NULL); if (ret) { src = dst->buf; len = dst->len; @@ -1561,7 +1556,7 @@ struct stream_filter { struct stream_filter_vtbl *vtbl; }; -static int null_filter_fn(struct stream_filter *filter, +static int null_filter_fn(struct stream_filter *filter UNUSED, const char *input, size_t *isize_p, char *output, size_t *osize_p) { @@ -1580,18 +1575,18 @@ static int null_filter_fn(struct stream_filter *filter, return 0; } -static void null_free_fn(struct stream_filter *filter) +static void null_free_fn(struct stream_filter *filter UNUSED) { ; /* nothing -- null instances are shared */ } static struct stream_filter_vtbl null_vtbl = { - null_filter_fn, - null_free_fn, + .filter = null_filter_fn, + .free = null_free_fn, }; static struct stream_filter null_filter_singleton = { - &null_vtbl, + .vtbl = &null_vtbl, }; int is_null_stream_filter(struct stream_filter *filter) @@ -1695,8 +1690,8 @@ static void lf_to_crlf_free_fn(struct stream_filter *filter) } static struct stream_filter_vtbl lf_to_crlf_vtbl = { - lf_to_crlf_filter_fn, - lf_to_crlf_free_fn, + .filter = lf_to_crlf_filter_fn, + .free = lf_to_crlf_free_fn, }; static struct stream_filter *lf_to_crlf_filter(void) @@ -1791,8 +1786,8 @@ static void cascade_free_fn(struct stream_filter *filter) } static struct stream_filter_vtbl cascade_vtbl = { - cascade_filter_fn, - cascade_free_fn, + .filter = cascade_filter_fn, + .free = cascade_free_fn, }; static struct stream_filter *cascade_filter(struct stream_filter *one, @@ -1943,8 +1938,8 @@ static void ident_free_fn(struct stream_filter *filter) } static struct stream_filter_vtbl ident_vtbl = { - ident_filter_fn, - ident_free_fn, + .filter = ident_filter_fn, + .free = ident_free_fn, }; static struct stream_filter *ident_filter(const struct object_id *oid) @@ -1960,34 +1955,25 @@ static struct stream_filter *ident_filter(const struct object_id *oid) } /* - * Return an appropriately constructed filter for the path, or NULL if + * Return an appropriately constructed filter for the given ca, or NULL if * the contents cannot be filtered without reading the whole thing * in-core. * * Note that you would be crazy to set CRLF, smudge/clean or ident to a * large binary blob you would want us not to slurp into the memory! */ -struct stream_filter *get_stream_filter(const struct index_state *istate, - const char *path, - const struct object_id *oid) +struct stream_filter *get_stream_filter_ca(const struct conv_attrs *ca, + const struct object_id *oid) { - struct conv_attrs ca; struct stream_filter *filter = NULL; - convert_attrs(istate, &ca, path); - if (ca.drv && (ca.drv->process || ca.drv->smudge || ca.drv->clean)) - return NULL; - - if (ca.working_tree_encoding) + if (classify_conv_attrs(ca) != CA_CLASS_STREAMABLE) return NULL; - if (ca.crlf_action == CRLF_AUTO || ca.crlf_action == CRLF_AUTO_CRLF) - return NULL; - - if (ca.ident) + if (ca->ident) filter = ident_filter(oid); - if (output_eol(ca.crlf_action) == EOL_CRLF) + if (output_eol(ca->crlf_action) == EOL_CRLF) filter = cascade_filter(filter, lf_to_crlf_filter()); else filter = cascade_filter(filter, &null_filter_singleton); @@ -1995,6 +1981,15 @@ struct stream_filter *get_stream_filter(const struct index_state *istate, return filter; } +struct stream_filter *get_stream_filter(struct index_state *istate, + const char *path, + const struct object_id *oid) +{ + struct conv_attrs ca; + convert_attrs(istate, &ca, path); + return get_stream_filter_ca(&ca, oid); +} + void free_stream_filter(struct stream_filter *filter) { filter->vtbl->free(filter); @@ -2028,3 +2023,21 @@ void clone_checkout_metadata(struct checkout_metadata *dst, if (blob) oidcpy(&dst->blob, blob); } + +enum conv_attrs_classification classify_conv_attrs(const struct conv_attrs *ca) +{ + if (ca->drv) { + if (ca->drv->process) + return CA_CLASS_INCORE_PROCESS; + if (ca->drv->smudge || ca->drv->clean) + return CA_CLASS_INCORE_FILTER; + } + + if (ca->working_tree_encoding) + return CA_CLASS_INCORE; + + if (ca->crlf_action == CRLF_AUTO || ca->crlf_action == CRLF_AUTO_CRLF) + return CA_CLASS_INCORE; + + return CA_CLASS_STREAMABLE; +} |