X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=convert.c;h=01de9a84c21b31a0120065a32a386f27321cdf7b;hb=a33fb40fe477fa1a9ce882230415f19378959390;hp=1144e0b4f5539172eb4043edc8013fb8e69f135d;hpb=fd6cce9e89ab5ac1125a3b5f5611048ad22379e7;p=git.git diff --git a/convert.c b/convert.c index 1144e0b4f..01de9a84c 100644 --- a/convert.c +++ b/convert.c @@ -8,7 +8,7 @@ * This should use the pathname to decide on whether it wants to do some * more interesting conversions (automatic gzip/unzip, general format * conversions etc etc), but by default it just does automatic CRLF<->LF - * translation when the "crlf" attribute or "auto_crlf" option is set. + * translation when the "text" attribute or "auto_crlf" option is set. */ enum action { @@ -20,12 +20,6 @@ enum action { CRLF_AUTO, }; -enum eol { - EOL_UNSET, - EOL_LF, - EOL_CRLF, -}; - struct text_stat { /* NUL, CR, LF and CRLF counts */ unsigned nul, cr, lf, crlf; @@ -99,33 +93,56 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } +static enum eol determine_output_conversion(enum action action) +{ + switch (action) { + case CRLF_BINARY: + return EOL_UNSET; + case CRLF_CRLF: + return EOL_CRLF; + case CRLF_INPUT: + return EOL_LF; + case CRLF_GUESS: + if (!auto_crlf) + return EOL_UNSET; + /* fall through */ + case CRLF_TEXT: + case CRLF_AUTO: + if (auto_crlf == AUTO_CRLF_TRUE) + return EOL_CRLF; + else if (auto_crlf == AUTO_CRLF_INPUT) + return EOL_LF; + else if (eol == EOL_UNSET) + return EOL_NATIVE; + } + return eol; +} + static void check_safe_crlf(const char *path, enum action action, struct text_stat *stats, enum safe_crlf checksafe) { if (!checksafe) return; - if (action == CRLF_INPUT || - (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_INPUT)) { + if (determine_output_conversion(action) == EOL_LF) { /* * CRLFs would not be restored by checkout: * check if we'd remove CRLFs */ if (stats->crlf) { if (checksafe == SAFE_CRLF_WARN) - warning("CRLF will be replaced by LF in %s.", path); + warning("CRLF will be replaced by LF in %s.\nThe file will have its original line endings in your working directory.", path); else /* i.e. SAFE_CRLF_FAIL */ die("CRLF would be replaced by LF in %s.", path); } - } else if (action == CRLF_CRLF || - (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_TRUE)) { + } else if (determine_output_conversion(action) == EOL_CRLF) { /* * CRLFs would be added by checkout: * check if we have "naked" LFs */ if (stats->lf != stats->crlf) { if (checksafe == SAFE_CRLF_WARN) - warning("LF will be replaced by CRLF in %s", path); + warning("LF will be replaced by CRLF in %s.\nThe file will have its original line endings in your working directory.", path); else /* i.e. SAFE_CRLF_FAIL */ die("LF would be replaced by CRLF in %s", path); } @@ -244,11 +261,7 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len, char *to_free = NULL; struct text_stat stats; - if ((action == CRLF_BINARY) || (action == CRLF_INPUT) || - (action != CRLF_CRLF && auto_crlf != AUTO_CRLF_TRUE)) - return 0; - - if (!len) + if (!len || determine_output_conversion(action) != EOL_CRLF) return 0; gather_stats(src, len, &stats); @@ -307,7 +320,7 @@ struct filter_params { const char *cmd; }; -static int filter_buffer(int fd, void *data) +static int filter_buffer(int in, int out, void *data) { /* * Spawn cmd and feed the buffer contents through its stdin. @@ -315,13 +328,15 @@ static int filter_buffer(int fd, void *data) struct child_process child_process; struct filter_params *params = (struct filter_params *)data; int write_err, status; - const char *argv[] = { params->cmd, NULL }; + const char *argv[] = { NULL, NULL }; + + argv[0] = params->cmd; memset(&child_process, 0, sizeof(child_process)); child_process.argv = argv; child_process.use_shell = 1; child_process.in = -1; - child_process.out = fd; + child_process.out = out; if (start_command(&child_process)) return error("cannot fork to run external filter %s", params->cmd); @@ -358,6 +373,7 @@ static int apply_filter(const char *path, const char *src, size_t len, memset(&async, 0, sizeof(async)); async.proc = filter_buffer; async.data = ¶ms; + async.out = -1; params.src = src; params.size = len; params.cmd = cmd; @@ -439,12 +455,14 @@ static int read_convert_config(const char *var, const char *value, void *cb) static void setup_convert_check(struct git_attr_check *check) { + static struct git_attr *attr_text; static struct git_attr *attr_crlf; static struct git_attr *attr_eol; static struct git_attr *attr_ident; static struct git_attr *attr_filter; - if (!attr_crlf) { + if (!attr_text) { + attr_text = git_attr("text"); attr_crlf = git_attr("crlf"); attr_eol = git_attr("eol"); attr_ident = git_attr("ident"); @@ -456,6 +474,7 @@ static void setup_convert_check(struct git_attr_check *check) check[1].attr = attr_ident; check[2].attr = attr_filter; check[3].attr = attr_eol; + check[4].attr = attr_text; } static int count_ident(const char *cp, unsigned long size) @@ -493,6 +512,8 @@ static int count_ident(const char *cp, unsigned long size) cnt++; break; } + if (ch == '\n') + break; } } return cnt; @@ -523,6 +544,11 @@ static int ident_to_git(const char *path, const char *src, size_t len, dollar = memchr(src + 3, '$', len - 3); if (!dollar) break; + if (memchr(src + 3, '\n', dollar - src - 3)) { + /* Line break before the next dollar. */ + continue; + } + memcpy(dst, "Id$", 3); dst += 3; len -= dollar + 1 - src; @@ -538,7 +564,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len, struct strbuf *buf, int ident) { unsigned char sha1[20]; - char *to_free = NULL, *dollar; + char *to_free = NULL, *dollar, *spc; int cnt; if (!ident) @@ -574,7 +600,10 @@ static int ident_to_worktree(const char *path, const char *src, size_t len, } else if (src[2] == ':') { /* * It's possible that an expanded Id has crept its way into the - * repository, we cope with that by stripping the expansion out + * repository, we cope with that by stripping the expansion out. + * This is probably not a good idea, since it will cause changes + * on checkout, which won't go away by stash, but let's keep it + * for git-style ids. */ dollar = memchr(src + 3, '$', len - 3); if (!dollar) { @@ -582,6 +611,20 @@ static int ident_to_worktree(const char *path, const char *src, size_t len, break; } + if (memchr(src + 3, '\n', dollar - src - 3)) { + /* Line break before the next dollar. */ + continue; + } + + spc = memchr(src + 4, ' ', dollar - src - 4); + if (spc && spc < dollar-1) { + /* There are spaces in unexpected places. + * This is probably an id from some other + * versioning system. Keep it for now. + */ + continue; + } + len -= dollar + 1 - src; src = dollar + 1; } else { @@ -651,32 +694,35 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check) return !!ATTR_TRUE(value); } -enum action determine_action(enum action crlf_attr, enum eol eol_attr) { - if (crlf_attr == CRLF_BINARY) +static enum action determine_action(enum action text_attr, enum eol eol_attr) +{ + if (text_attr == CRLF_BINARY) return CRLF_BINARY; if (eol_attr == EOL_LF) return CRLF_INPUT; if (eol_attr == EOL_CRLF) return CRLF_CRLF; - return crlf_attr; + return text_attr; } int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst, enum safe_crlf checksafe) { - struct git_attr_check check[4]; + struct git_attr_check check[5]; enum action action = CRLF_GUESS; - enum eol eol = EOL_UNSET; + enum eol eol_attr = EOL_UNSET; int ident = 0, ret = 0; const char *filter = NULL; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { struct convert_driver *drv; - action = git_path_check_crlf(path, check + 0); + action = git_path_check_crlf(path, check + 4); + if (action == CRLF_GUESS) + action = git_path_check_crlf(path, check + 0); ident = git_path_check_ident(path, check + 1); drv = git_path_check_convert(path, check + 2); - eol = git_path_check_eol(path, check + 3); + eol_attr = git_path_check_eol(path, check + 3); if (drv && drv->clean) filter = drv->clean; } @@ -686,7 +732,7 @@ int convert_to_git(const char *path, const char *src, size_t len, src = dst->buf; len = dst->len; } - action = determine_action(action, eol); + action = determine_action(action, eol_attr); ret |= crlf_to_git(path, src, len, dst, action, checksafe); if (ret) { src = dst->buf; @@ -695,21 +741,25 @@ int convert_to_git(const char *path, const char *src, size_t len, return ret | ident_to_git(path, src, len, dst, ident); } -int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst) +static int convert_to_working_tree_internal(const char *path, const char *src, + size_t len, struct strbuf *dst, + int normalizing) { - struct git_attr_check check[4]; + struct git_attr_check check[5]; enum action action = CRLF_GUESS; - enum eol eol = EOL_UNSET; + enum eol eol_attr = EOL_UNSET; int ident = 0, ret = 0; const char *filter = NULL; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { struct convert_driver *drv; - action = git_path_check_crlf(path, check + 0); + action = git_path_check_crlf(path, check + 4); + if (action == CRLF_GUESS) + action = git_path_check_crlf(path, check + 0); ident = git_path_check_ident(path, check + 1); drv = git_path_check_convert(path, check + 2); - eol = git_path_check_eol(path, check + 3); + eol_attr = git_path_check_eol(path, check + 3); if (drv && drv->smudge) filter = drv->smudge; } @@ -719,11 +769,32 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc src = dst->buf; len = dst->len; } - action = determine_action(action, eol); - ret |= crlf_to_worktree(path, src, len, dst, action); + /* + * CRLF conversion can be skipped if normalizing, unless there + * is a smudge filter. The filter might expect CRLFs. + */ + if (filter || !normalizing) { + action = determine_action(action, eol_attr); + ret |= crlf_to_worktree(path, src, len, dst, action); + if (ret) { + src = dst->buf; + len = dst->len; + } + } + return ret | apply_filter(path, src, len, dst, filter); +} + +int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst) +{ + return convert_to_working_tree_internal(path, src, len, dst, 0); +} + +int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst) +{ + int ret = convert_to_working_tree_internal(path, src, len, dst, 1); if (ret) { src = dst->buf; len = dst->len; } - return ret | apply_filter(path, src, len, dst, filter); + return ret | convert_to_git(path, src, len, dst, 0); }