convert.con commit Add -e/--exclude to git-clean. (07de4eb)
   1#include "cache.h"
   2#include "attr.h"
   3#include "run-command.h"
   4
   5/*
   6 * convert.c - convert a file when checking it out and checking it in.
   7 *
   8 * This should use the pathname to decide on whether it wants to do some
   9 * more interesting conversions (automatic gzip/unzip, general format
  10 * conversions etc etc), but by default it just does automatic CRLF<->LF
  11 * translation when the "text" attribute or "auto_crlf" option is set.
  12 */
  13
  14enum action {
  15        CRLF_GUESS = -1,
  16        CRLF_BINARY = 0,
  17        CRLF_TEXT,
  18        CRLF_INPUT,
  19        CRLF_CRLF,
  20        CRLF_AUTO,
  21};
  22
  23struct text_stat {
  24        /* NUL, CR, LF and CRLF counts */
  25        unsigned nul, cr, lf, crlf;
  26
  27        /* These are just approximations! */
  28        unsigned printable, nonprintable;
  29};
  30
  31static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
  32{
  33        unsigned long i;
  34
  35        memset(stats, 0, sizeof(*stats));
  36
  37        for (i = 0; i < size; i++) {
  38                unsigned char c = buf[i];
  39                if (c == '\r') {
  40                        stats->cr++;
  41                        if (i+1 < size && buf[i+1] == '\n')
  42                                stats->crlf++;
  43                        continue;
  44                }
  45                if (c == '\n') {
  46                        stats->lf++;
  47                        continue;
  48                }
  49                if (c == 127)
  50                        /* DEL */
  51                        stats->nonprintable++;
  52                else if (c < 32) {
  53                        switch (c) {
  54                                /* BS, HT, ESC and FF */
  55                        case '\b': case '\t': case '\033': case '\014':
  56                                stats->printable++;
  57                                break;
  58                        case 0:
  59                                stats->nul++;
  60                                /* fall through */
  61                        default:
  62                                stats->nonprintable++;
  63                        }
  64                }
  65                else
  66                        stats->printable++;
  67        }
  68
  69        /* If file ends with EOF then don't count this EOF as non-printable. */
  70        if (size >= 1 && buf[size-1] == '\032')
  71                stats->nonprintable--;
  72}
  73
  74/*
  75 * The same heuristics as diff.c::mmfile_is_binary()
  76 */
  77static int is_binary(unsigned long size, struct text_stat *stats)
  78{
  79
  80        if (stats->nul)
  81                return 1;
  82        if ((stats->printable >> 7) < stats->nonprintable)
  83                return 1;
  84        /*
  85         * Other heuristics? Average line length might be relevant,
  86         * as might LF vs CR vs CRLF counts..
  87         *
  88         * NOTE! It might be normal to have a low ratio of CRLF to LF
  89         * (somebody starts with a LF-only file and edits it with an editor
  90         * that adds CRLF only to lines that are added..). But do  we
  91         * want to support CR-only? Probably not.
  92         */
  93        return 0;
  94}
  95
  96static enum eol determine_output_conversion(enum action action) {
  97        switch (action) {
  98        case CRLF_BINARY:
  99                return EOL_UNSET;
 100        case CRLF_CRLF:
 101                return EOL_CRLF;
 102        case CRLF_INPUT:
 103                return EOL_LF;
 104        case CRLF_GUESS:
 105                if (!auto_crlf)
 106                        return EOL_UNSET;
 107                /* fall through */
 108        case CRLF_TEXT:
 109        case CRLF_AUTO:
 110                if (auto_crlf == AUTO_CRLF_TRUE)
 111                        return EOL_CRLF;
 112                else if (auto_crlf == AUTO_CRLF_INPUT)
 113                        return EOL_LF;
 114                else if (eol == EOL_UNSET)
 115                        return EOL_NATIVE;
 116        }
 117        return eol;
 118}
 119
 120static void check_safe_crlf(const char *path, enum action action,
 121                            struct text_stat *stats, enum safe_crlf checksafe)
 122{
 123        if (!checksafe)
 124                return;
 125
 126        if (determine_output_conversion(action) == EOL_LF) {
 127                /*
 128                 * CRLFs would not be restored by checkout:
 129                 * check if we'd remove CRLFs
 130                 */
 131                if (stats->crlf) {
 132                        if (checksafe == SAFE_CRLF_WARN)
 133                                warning("CRLF will be replaced by LF in %s.\nThe file will have its original line endings in your working directory.", path);
 134                        else /* i.e. SAFE_CRLF_FAIL */
 135                                die("CRLF would be replaced by LF in %s.", path);
 136                }
 137        } else if (determine_output_conversion(action) == EOL_CRLF) {
 138                /*
 139                 * CRLFs would be added by checkout:
 140                 * check if we have "naked" LFs
 141                 */
 142                if (stats->lf != stats->crlf) {
 143                        if (checksafe == SAFE_CRLF_WARN)
 144                                warning("LF will be replaced by CRLF in %s.\nThe file will have its original line endings in your working directory.", path);
 145                        else /* i.e. SAFE_CRLF_FAIL */
 146                                die("LF would be replaced by CRLF in %s", path);
 147                }
 148        }
 149}
 150
 151static int has_cr_in_index(const char *path)
 152{
 153        int pos, len;
 154        unsigned long sz;
 155        enum object_type type;
 156        void *data;
 157        int has_cr;
 158        struct index_state *istate = &the_index;
 159
 160        len = strlen(path);
 161        pos = index_name_pos(istate, path, len);
 162        if (pos < 0) {
 163                /*
 164                 * We might be in the middle of a merge, in which
 165                 * case we would read stage #2 (ours).
 166                 */
 167                int i;
 168                for (i = -pos - 1;
 169                     (pos < 0 && i < istate->cache_nr &&
 170                      !strcmp(istate->cache[i]->name, path));
 171                     i++)
 172                        if (ce_stage(istate->cache[i]) == 2)
 173                                pos = i;
 174        }
 175        if (pos < 0)
 176                return 0;
 177        data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
 178        if (!data || type != OBJ_BLOB) {
 179                free(data);
 180                return 0;
 181        }
 182
 183        has_cr = memchr(data, '\r', sz) != NULL;
 184        free(data);
 185        return has_cr;
 186}
 187
 188static int crlf_to_git(const char *path, const char *src, size_t len,
 189                       struct strbuf *buf, enum action action, enum safe_crlf checksafe)
 190{
 191        struct text_stat stats;
 192        char *dst;
 193
 194        if (action == CRLF_BINARY ||
 195            (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE) || !len)
 196                return 0;
 197
 198        gather_stats(src, len, &stats);
 199
 200        if (action == CRLF_AUTO || action == CRLF_GUESS) {
 201                /*
 202                 * We're currently not going to even try to convert stuff
 203                 * that has bare CR characters. Does anybody do that crazy
 204                 * stuff?
 205                 */
 206                if (stats.cr != stats.crlf)
 207                        return 0;
 208
 209                /*
 210                 * And add some heuristics for binary vs text, of course...
 211                 */
 212                if (is_binary(len, &stats))
 213                        return 0;
 214
 215                if (action == CRLF_GUESS) {
 216                        /*
 217                         * If the file in the index has any CR in it, do not convert.
 218                         * This is the new safer autocrlf handling.
 219                         */
 220                        if (has_cr_in_index(path))
 221                                return 0;
 222                }
 223        }
 224
 225        check_safe_crlf(path, action, &stats, checksafe);
 226
 227        /* Optimization: No CR? Nothing to convert, regardless. */
 228        if (!stats.cr)
 229                return 0;
 230
 231        /* only grow if not in place */
 232        if (strbuf_avail(buf) + buf->len < len)
 233                strbuf_grow(buf, len - buf->len);
 234        dst = buf->buf;
 235        if (action == CRLF_AUTO || action == CRLF_GUESS) {
 236                /*
 237                 * If we guessed, we already know we rejected a file with
 238                 * lone CR, and we can strip a CR without looking at what
 239                 * follow it.
 240                 */
 241                do {
 242                        unsigned char c = *src++;
 243                        if (c != '\r')
 244                                *dst++ = c;
 245                } while (--len);
 246        } else {
 247                do {
 248                        unsigned char c = *src++;
 249                        if (! (c == '\r' && (1 < len && *src == '\n')))
 250                                *dst++ = c;
 251                } while (--len);
 252        }
 253        strbuf_setlen(buf, dst - buf->buf);
 254        return 1;
 255}
 256
 257static int crlf_to_worktree(const char *path, const char *src, size_t len,
 258                            struct strbuf *buf, enum action action)
 259{
 260        char *to_free = NULL;
 261        struct text_stat stats;
 262
 263        if (!len || determine_output_conversion(action) != EOL_CRLF)
 264                return 0;
 265
 266        gather_stats(src, len, &stats);
 267
 268        /* No LF? Nothing to convert, regardless. */
 269        if (!stats.lf)
 270                return 0;
 271
 272        /* Was it already in CRLF format? */
 273        if (stats.lf == stats.crlf)
 274                return 0;
 275
 276        if (action == CRLF_AUTO || action == CRLF_GUESS) {
 277                if (action == CRLF_GUESS) {
 278                        /* If we have any CR or CRLF line endings, we do not touch it */
 279                        /* This is the new safer autocrlf-handling */
 280                        if (stats.cr > 0 || stats.crlf > 0)
 281                                return 0;
 282                }
 283
 284                /* If we have any bare CR characters, we're not going to touch it */
 285                if (stats.cr != stats.crlf)
 286                        return 0;
 287
 288                if (is_binary(len, &stats))
 289                        return 0;
 290        }
 291
 292        /* are we "faking" in place editing ? */
 293        if (src == buf->buf)
 294                to_free = strbuf_detach(buf, NULL);
 295
 296        strbuf_grow(buf, len + stats.lf - stats.crlf);
 297        for (;;) {
 298                const char *nl = memchr(src, '\n', len);
 299                if (!nl)
 300                        break;
 301                if (nl > src && nl[-1] == '\r') {
 302                        strbuf_add(buf, src, nl + 1 - src);
 303                } else {
 304                        strbuf_add(buf, src, nl - src);
 305                        strbuf_addstr(buf, "\r\n");
 306                }
 307                len -= nl + 1 - src;
 308                src  = nl + 1;
 309        }
 310        strbuf_add(buf, src, len);
 311
 312        free(to_free);
 313        return 1;
 314}
 315
 316struct filter_params {
 317        const char *src;
 318        unsigned long size;
 319        const char *cmd;
 320};
 321
 322static int filter_buffer(int in, int out, void *data)
 323{
 324        /*
 325         * Spawn cmd and feed the buffer contents through its stdin.
 326         */
 327        struct child_process child_process;
 328        struct filter_params *params = (struct filter_params *)data;
 329        int write_err, status;
 330        const char *argv[] = { NULL, NULL };
 331
 332        argv[0] = params->cmd;
 333
 334        memset(&child_process, 0, sizeof(child_process));
 335        child_process.argv = argv;
 336        child_process.use_shell = 1;
 337        child_process.in = -1;
 338        child_process.out = out;
 339
 340        if (start_command(&child_process))
 341                return error("cannot fork to run external filter %s", params->cmd);
 342
 343        write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
 344        if (close(child_process.in))
 345                write_err = 1;
 346        if (write_err)
 347                error("cannot feed the input to external filter %s", params->cmd);
 348
 349        status = finish_command(&child_process);
 350        if (status)
 351                error("external filter %s failed %d", params->cmd, status);
 352        return (write_err || status);
 353}
 354
 355static int apply_filter(const char *path, const char *src, size_t len,
 356                        struct strbuf *dst, const char *cmd)
 357{
 358        /*
 359         * Create a pipeline to have the command filter the buffer's
 360         * contents.
 361         *
 362         * (child --> cmd) --> us
 363         */
 364        int ret = 1;
 365        struct strbuf nbuf = STRBUF_INIT;
 366        struct async async;
 367        struct filter_params params;
 368
 369        if (!cmd)
 370                return 0;
 371
 372        memset(&async, 0, sizeof(async));
 373        async.proc = filter_buffer;
 374        async.data = &params;
 375        async.out = -1;
 376        params.src = src;
 377        params.size = len;
 378        params.cmd = cmd;
 379
 380        fflush(NULL);
 381        if (start_async(&async))
 382                return 0;       /* error was already reported */
 383
 384        if (strbuf_read(&nbuf, async.out, len) < 0) {
 385                error("read from external filter %s failed", cmd);
 386                ret = 0;
 387        }
 388        if (close(async.out)) {
 389                error("read from external filter %s failed", cmd);
 390                ret = 0;
 391        }
 392        if (finish_async(&async)) {
 393                error("external filter %s failed", cmd);
 394                ret = 0;
 395        }
 396
 397        if (ret) {
 398                strbuf_swap(dst, &nbuf);
 399        }
 400        strbuf_release(&nbuf);
 401        return ret;
 402}
 403
 404static struct convert_driver {
 405        const char *name;
 406        struct convert_driver *next;
 407        const char *smudge;
 408        const char *clean;
 409} *user_convert, **user_convert_tail;
 410
 411static int read_convert_config(const char *var, const char *value, void *cb)
 412{
 413        const char *ep, *name;
 414        int namelen;
 415        struct convert_driver *drv;
 416
 417        /*
 418         * External conversion drivers are configured using
 419         * "filter.<name>.variable".
 420         */
 421        if (prefixcmp(var, "filter.") || (ep = strrchr(var, '.')) == var + 6)
 422                return 0;
 423        name = var + 7;
 424        namelen = ep - name;
 425        for (drv = user_convert; drv; drv = drv->next)
 426                if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
 427                        break;
 428        if (!drv) {
 429                drv = xcalloc(1, sizeof(struct convert_driver));
 430                drv->name = xmemdupz(name, namelen);
 431                *user_convert_tail = drv;
 432                user_convert_tail = &(drv->next);
 433        }
 434
 435        ep++;
 436
 437        /*
 438         * filter.<name>.smudge and filter.<name>.clean specifies
 439         * the command line:
 440         *
 441         *      command-line
 442         *
 443         * The command-line will not be interpolated in any way.
 444         */
 445
 446        if (!strcmp("smudge", ep))
 447                return git_config_string(&drv->smudge, var, value);
 448
 449        if (!strcmp("clean", ep))
 450                return git_config_string(&drv->clean, var, value);
 451
 452        return 0;
 453}
 454
 455static void setup_convert_check(struct git_attr_check *check)
 456{
 457        static struct git_attr *attr_text;
 458        static struct git_attr *attr_crlf;
 459        static struct git_attr *attr_eol;
 460        static struct git_attr *attr_ident;
 461        static struct git_attr *attr_filter;
 462
 463        if (!attr_text) {
 464                attr_text = git_attr("text");
 465                attr_crlf = git_attr("crlf");
 466                attr_eol = git_attr("eol");
 467                attr_ident = git_attr("ident");
 468                attr_filter = git_attr("filter");
 469                user_convert_tail = &user_convert;
 470                git_config(read_convert_config, NULL);
 471        }
 472        check[0].attr = attr_crlf;
 473        check[1].attr = attr_ident;
 474        check[2].attr = attr_filter;
 475        check[3].attr = attr_eol;
 476        check[4].attr = attr_text;
 477}
 478
 479static int count_ident(const char *cp, unsigned long size)
 480{
 481        /*
 482         * "$Id: 0000000000000000000000000000000000000000 $" <=> "$Id$"
 483         */
 484        int cnt = 0;
 485        char ch;
 486
 487        while (size) {
 488                ch = *cp++;
 489                size--;
 490                if (ch != '$')
 491                        continue;
 492                if (size < 3)
 493                        break;
 494                if (memcmp("Id", cp, 2))
 495                        continue;
 496                ch = cp[2];
 497                cp += 3;
 498                size -= 3;
 499                if (ch == '$')
 500                        cnt++; /* $Id$ */
 501                if (ch != ':')
 502                        continue;
 503
 504                /*
 505                 * "$Id: ... "; scan up to the closing dollar sign and discard.
 506                 */
 507                while (size) {
 508                        ch = *cp++;
 509                        size--;
 510                        if (ch == '$') {
 511                                cnt++;
 512                                break;
 513                        }
 514                        if (ch == '\n')
 515                                break;
 516                }
 517        }
 518        return cnt;
 519}
 520
 521static int ident_to_git(const char *path, const char *src, size_t len,
 522                        struct strbuf *buf, int ident)
 523{
 524        char *dst, *dollar;
 525
 526        if (!ident || !count_ident(src, len))
 527                return 0;
 528
 529        /* only grow if not in place */
 530        if (strbuf_avail(buf) + buf->len < len)
 531                strbuf_grow(buf, len - buf->len);
 532        dst = buf->buf;
 533        for (;;) {
 534                dollar = memchr(src, '$', len);
 535                if (!dollar)
 536                        break;
 537                memcpy(dst, src, dollar + 1 - src);
 538                dst += dollar + 1 - src;
 539                len -= dollar + 1 - src;
 540                src  = dollar + 1;
 541
 542                if (len > 3 && !memcmp(src, "Id:", 3)) {
 543                        dollar = memchr(src + 3, '$', len - 3);
 544                        if (!dollar)
 545                                break;
 546                        if (memchr(src + 3, '\n', dollar - src - 3)) {
 547                                /* Line break before the next dollar. */
 548                                continue;
 549                        }
 550
 551                        memcpy(dst, "Id$", 3);
 552                        dst += 3;
 553                        len -= dollar + 1 - src;
 554                        src  = dollar + 1;
 555                }
 556        }
 557        memcpy(dst, src, len);
 558        strbuf_setlen(buf, dst + len - buf->buf);
 559        return 1;
 560}
 561
 562static int ident_to_worktree(const char *path, const char *src, size_t len,
 563                             struct strbuf *buf, int ident)
 564{
 565        unsigned char sha1[20];
 566        char *to_free = NULL, *dollar, *spc;
 567        int cnt;
 568
 569        if (!ident)
 570                return 0;
 571
 572        cnt = count_ident(src, len);
 573        if (!cnt)
 574                return 0;
 575
 576        /* are we "faking" in place editing ? */
 577        if (src == buf->buf)
 578                to_free = strbuf_detach(buf, NULL);
 579        hash_sha1_file(src, len, "blob", sha1);
 580
 581        strbuf_grow(buf, len + cnt * 43);
 582        for (;;) {
 583                /* step 1: run to the next '$' */
 584                dollar = memchr(src, '$', len);
 585                if (!dollar)
 586                        break;
 587                strbuf_add(buf, src, dollar + 1 - src);
 588                len -= dollar + 1 - src;
 589                src  = dollar + 1;
 590
 591                /* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */
 592                if (len < 3 || memcmp("Id", src, 2))
 593                        continue;
 594
 595                /* step 3: skip over Id$ or Id:xxxxx$ */
 596                if (src[2] == '$') {
 597                        src += 3;
 598                        len -= 3;
 599                } else if (src[2] == ':') {
 600                        /*
 601                         * It's possible that an expanded Id has crept its way into the
 602                         * repository, we cope with that by stripping the expansion out.
 603                         * This is probably not a good idea, since it will cause changes
 604                         * on checkout, which won't go away by stash, but let's keep it
 605                         * for git-style ids.
 606                         */
 607                        dollar = memchr(src + 3, '$', len - 3);
 608                        if (!dollar) {
 609                                /* incomplete keyword, no more '$', so just quit the loop */
 610                                break;
 611                        }
 612
 613                        if (memchr(src + 3, '\n', dollar - src - 3)) {
 614                                /* Line break before the next dollar. */
 615                                continue;
 616                        }
 617
 618                        spc = memchr(src + 4, ' ', dollar - src - 4);
 619                        if (spc && spc < dollar-1) {
 620                                /* There are spaces in unexpected places.
 621                                 * This is probably an id from some other
 622                                 * versioning system. Keep it for now.
 623                                 */
 624                                continue;
 625                        }
 626
 627                        len -= dollar + 1 - src;
 628                        src  = dollar + 1;
 629                } else {
 630                        /* it wasn't a "Id$" or "Id:xxxx$" */
 631                        continue;
 632                }
 633
 634                /* step 4: substitute */
 635                strbuf_addstr(buf, "Id: ");
 636                strbuf_add(buf, sha1_to_hex(sha1), 40);
 637                strbuf_addstr(buf, " $");
 638        }
 639        strbuf_add(buf, src, len);
 640
 641        free(to_free);
 642        return 1;
 643}
 644
 645static int git_path_check_crlf(const char *path, struct git_attr_check *check)
 646{
 647        const char *value = check->value;
 648
 649        if (ATTR_TRUE(value))
 650                return CRLF_TEXT;
 651        else if (ATTR_FALSE(value))
 652                return CRLF_BINARY;
 653        else if (ATTR_UNSET(value))
 654                ;
 655        else if (!strcmp(value, "input"))
 656                return CRLF_INPUT;
 657        else if (!strcmp(value, "auto"))
 658                return CRLF_AUTO;
 659        return CRLF_GUESS;
 660}
 661
 662static int git_path_check_eol(const char *path, struct git_attr_check *check)
 663{
 664        const char *value = check->value;
 665
 666        if (ATTR_UNSET(value))
 667                ;
 668        else if (!strcmp(value, "lf"))
 669                return EOL_LF;
 670        else if (!strcmp(value, "crlf"))
 671                return EOL_CRLF;
 672        return EOL_UNSET;
 673}
 674
 675static struct convert_driver *git_path_check_convert(const char *path,
 676                                             struct git_attr_check *check)
 677{
 678        const char *value = check->value;
 679        struct convert_driver *drv;
 680
 681        if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
 682                return NULL;
 683        for (drv = user_convert; drv; drv = drv->next)
 684                if (!strcmp(value, drv->name))
 685                        return drv;
 686        return NULL;
 687}
 688
 689static int git_path_check_ident(const char *path, struct git_attr_check *check)
 690{
 691        const char *value = check->value;
 692
 693        return !!ATTR_TRUE(value);
 694}
 695
 696enum action determine_action(enum action text_attr, enum eol eol_attr) {
 697        if (text_attr == CRLF_BINARY)
 698                return CRLF_BINARY;
 699        if (eol_attr == EOL_LF)
 700                return CRLF_INPUT;
 701        if (eol_attr == EOL_CRLF)
 702                return CRLF_CRLF;
 703        return text_attr;
 704}
 705
 706int convert_to_git(const char *path, const char *src, size_t len,
 707                   struct strbuf *dst, enum safe_crlf checksafe)
 708{
 709        struct git_attr_check check[5];
 710        enum action action = CRLF_GUESS;
 711        enum eol eol_attr = EOL_UNSET;
 712        int ident = 0, ret = 0;
 713        const char *filter = NULL;
 714
 715        setup_convert_check(check);
 716        if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
 717                struct convert_driver *drv;
 718                action = git_path_check_crlf(path, check + 4);
 719                if (action == CRLF_GUESS)
 720                        action = git_path_check_crlf(path, check + 0);
 721                ident = git_path_check_ident(path, check + 1);
 722                drv = git_path_check_convert(path, check + 2);
 723                eol_attr = git_path_check_eol(path, check + 3);
 724                if (drv && drv->clean)
 725                        filter = drv->clean;
 726        }
 727
 728        ret |= apply_filter(path, src, len, dst, filter);
 729        if (ret) {
 730                src = dst->buf;
 731                len = dst->len;
 732        }
 733        action = determine_action(action, eol_attr);
 734        ret |= crlf_to_git(path, src, len, dst, action, checksafe);
 735        if (ret) {
 736                src = dst->buf;
 737                len = dst->len;
 738        }
 739        return ret | ident_to_git(path, src, len, dst, ident);
 740}
 741
 742int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
 743{
 744        struct git_attr_check check[5];
 745        enum action action = CRLF_GUESS;
 746        enum eol eol_attr = EOL_UNSET;
 747        int ident = 0, ret = 0;
 748        const char *filter = NULL;
 749
 750        setup_convert_check(check);
 751        if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
 752                struct convert_driver *drv;
 753                action = git_path_check_crlf(path, check + 4);
 754                if (action == CRLF_GUESS)
 755                        action = git_path_check_crlf(path, check + 0);
 756                ident = git_path_check_ident(path, check + 1);
 757                drv = git_path_check_convert(path, check + 2);
 758                eol_attr = git_path_check_eol(path, check + 3);
 759                if (drv && drv->smudge)
 760                        filter = drv->smudge;
 761        }
 762
 763        ret |= ident_to_worktree(path, src, len, dst, ident);
 764        if (ret) {
 765                src = dst->buf;
 766                len = dst->len;
 767        }
 768        action = determine_action(action, eol_attr);
 769        ret |= crlf_to_worktree(path, src, len, dst, action);
 770        if (ret) {
 771                src = dst->buf;
 772                len = dst->len;
 773        }
 774        return ret | apply_filter(path, src, len, dst, filter);
 775}