From: Junio C Hamano Date: Sun, 17 Feb 2008 01:59:20 +0000 (-0800) Subject: Merge branch 'sp/safecrlf' X-Git-Tag: v1.5.5-rc0~206 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/2ac4b4b2228f3ef996db7b07aea74c4b1a796f38?ds=inline;hp=-c Merge branch 'sp/safecrlf' * sp/safecrlf: safecrlf: Add mechanism to warn about irreversible crlf conversions --- 2ac4b4b2228f3ef996db7b07aea74c4b1a796f38 diff --combined Documentation/config.txt index f9bdb164e0,7b8cae1541..f2f6a774e0 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -139,6 -139,51 +139,51 @@@ core.autocrlf: "text" (i.e. be subjected to the autocrlf mechanism) is decided purely based on the contents. + core.safecrlf:: + If true, makes git check if converting `CRLF` as controlled by + `core.autocrlf` is reversible. Git will verify if a command + modifies a file in the work tree either directly or indirectly. + For example, committing a file followed by checking out the + same file should yield the original file in the work tree. If + this is not the case for the current setting of + `core.autocrlf`, git will reject the file. The variable can + be set to "warn", in which case git will only warn about an + irreversible conversion but continue the operation. + + + CRLF conversion bears a slight chance of corrupting data. + autocrlf=true will convert CRLF to LF during commit and LF to + CRLF during checkout. A file that contains a mixture of LF and + CRLF before the commit cannot be recreated by git. For text + files this is the right thing to do: it corrects line endings + such that we have only LF line endings in the repository. + But for binary files that are accidentally classified as text the + conversion can corrupt data. + + + If you recognize such corruption early you can easily fix it by + setting the conversion type explicitly in .gitattributes. Right + after committing you still have the original file in your work + tree and this file is not yet corrupted. You can explicitly tell + git that this file is binary and git will handle the file + appropriately. + + + Unfortunately, the desired effect of cleaning up text files with + mixed line endings and the undesired effect of corrupting binary + files cannot be distinguished. In both cases CRLFs are removed + in an irreversible way. For text files this is the right thing + to do because CRLFs are line endings, while for binary files + converting CRLFs corrupts data. + + + Note, this safety check does not mean that a checkout will generate a + file identical to the original file for a different setting of + `core.autocrlf`, but only for the current one. For example, a text + file with `LF` would be accepted with `core.autocrlf=input` and could + later be checked out with `core.autocrlf=true`, in which case the + resulting file would contain `CRLF`, although the original file + contained `LF`. However, in both work trees the line endings would be + consistent, that is either all `LF` or all `CRLF`, but never mixed. A + file with mixed line endings would be reported by the `core.safecrlf` + mechanism. + core.symlinks:: If false, symbolic links are checked out as small plain files that contain the link text. linkgit:git-update-index[1] and @@@ -333,7 -378,7 +378,7 @@@ branch.autosetupmerge: so that linkgit:git-pull[1] will appropriately merge from that remote branch. Note that even if this option is not set, this behavior can be chosen per-branch using the `--track` - and `--no-track` options. This option defaults to false. + and `--no-track` options. This option defaults to true. branch..remote:: When in branch , it tells `git fetch` which remote to fetch. @@@ -766,12 -811,6 +811,12 @@@ pack.indexVersion: whenever the corresponding pack is larger than 2 GB. Otherwise the default is 1. +pack.packSizeLimit: + The default maximum size of a pack. This setting only affects + packing to a file, i.e. the git:// protocol is unaffected. It + can be overridden by the `\--max-pack-size` option of + linkgit:git-repack[1]. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --combined builtin-apply.c index 46dad5b2a1,3b5618d434..6a88ff018d --- a/builtin-apply.c +++ b/builtin-apply.c @@@ -1430,7 -1430,7 +1430,7 @@@ static int read_old_data(struct stat *s case S_IFREG: if (strbuf_read_file(buf, path, st->st_size) != st->st_size) return error("unable to open or read %s", path); - convert_to_git(path, buf->buf, buf->len, buf); + convert_to_git(path, buf->buf, buf->len, buf, 0); return 0; default: return -1; @@@ -1946,7 -1946,7 +1946,7 @@@ static int read_file_or_gitlink(struct if (!ce) return 0; - if (S_ISGITLINK(ntohl(ce->ce_mode))) { + if (S_ISGITLINK(ce->ce_mode)) { strbuf_grow(buf, 100); strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1)); } else { @@@ -2023,7 -2023,7 +2023,7 @@@ static int check_to_create_blob(const c static int verify_index_match(struct cache_entry *ce, struct stat *st) { - if (S_ISGITLINK(ntohl(ce->ce_mode))) { + if (S_ISGITLINK(ce->ce_mode)) { if (!S_ISDIR(st->st_mode)) return -1; return 0; @@@ -2082,12 -2082,12 +2082,12 @@@ static int check_patch(struct patch *pa return error("%s: does not match index", old_name); if (cached) - st_mode = ntohl(ce->ce_mode); + st_mode = ce->ce_mode; } else if (stat_ret < 0) return error("%s: %s", old_name, strerror(errno)); if (!cached) - st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode)); + st_mode = ce_mode_from_stat(ce, st.st_mode); if (patch->is_new < 0) patch->is_new = 0; @@@ -2388,7 -2388,7 +2388,7 @@@ static void add_index_file(const char * ce = xcalloc(1, ce_size); memcpy(ce->name, path, namelen); ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = htons(namelen); + ce->ce_flags = namelen; if (S_ISGITLINK(mode)) { const char *s = buf; @@@ -2746,8 -2746,6 +2746,8 @@@ static int apply_patch(int fd, const ch static int git_apply_config(const char *var, const char *value) { if (!strcmp(var, "apply.whitespace")) { + if (!value) + return config_error_nonbool(var); apply_default_whitespace = xstrdup(value); return 0; } diff --combined builtin-blame.c index c7e68874e7,c361ee194e..1cf254dcca --- a/builtin-blame.c +++ b/builtin-blame.c @@@ -2073,7 -2073,7 +2073,7 @@@ static struct commit *fake_working_tree if (strbuf_read(&buf, 0, 0) < 0) die("read error %s from stdin", strerror(errno)); } - convert_to_git(path, buf.buf, buf.len, &buf); + convert_to_git(path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); @@@ -2092,7 -2092,7 +2092,7 @@@ if (!mode) { int pos = cache_name_pos(path, len); if (0 <= pos) - mode = ntohl(active_cache[pos]->ce_mode); + mode = active_cache[pos]->ce_mode; else /* Let's not bother reading from HEAD tree */ mode = S_IFREG | 0644; diff --combined cache.h index 18fe8447f3,6003c83b2d..e1000bccb2 --- a/cache.h +++ b/cache.h @@@ -3,7 -3,6 +3,7 @@@ #include "git-compat-util.h" #include "strbuf.h" +#include "hash.h" #include SHA1_HEADER #include @@@ -95,116 -94,66 +95,116 @@@ struct cache_time * We save the fields in big-endian order to allow using the * index file over NFS transparently. */ +struct ondisk_cache_entry { + struct cache_time ctime; + struct cache_time mtime; + unsigned int dev; + unsigned int ino; + unsigned int mode; + unsigned int uid; + unsigned int gid; + unsigned int size; + unsigned char sha1[20]; + unsigned short flags; + char name[FLEX_ARRAY]; /* more */ +}; + struct cache_entry { - struct cache_time ce_ctime; - struct cache_time ce_mtime; + struct cache_entry *next; + unsigned int ce_ctime; + unsigned int ce_mtime; unsigned int ce_dev; unsigned int ce_ino; unsigned int ce_mode; unsigned int ce_uid; unsigned int ce_gid; unsigned int ce_size; + unsigned int ce_flags; unsigned char sha1[20]; - unsigned short ce_flags; char name[FLEX_ARRAY]; /* more */ }; #define CE_NAMEMASK (0x0fff) #define CE_STAGEMASK (0x3000) -#define CE_UPDATE (0x4000) #define CE_VALID (0x8000) #define CE_STAGESHIFT 12 -#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT)) -#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags)) +/* In-memory only */ +#define CE_UPDATE (0x10000) +#define CE_REMOVE (0x20000) +#define CE_UPTODATE (0x40000) +#define CE_UNHASHED (0x80000) + +static inline unsigned create_ce_flags(size_t len, unsigned stage) +{ + if (len >= CE_NAMEMASK) + len = CE_NAMEMASK; + return (len | (stage << CE_STAGESHIFT)); +} + +static inline size_t ce_namelen(const struct cache_entry *ce) +{ + size_t len = ce->ce_flags & CE_NAMEMASK; + if (len < CE_NAMEMASK) + return len; + return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK; +} + #define ce_size(ce) cache_entry_size(ce_namelen(ce)) -#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT) +#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce)) +#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT) +#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE) +#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE) #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644) static inline unsigned int create_ce_mode(unsigned int mode) { if (S_ISLNK(mode)) - return htonl(S_IFLNK); + return S_IFLNK; if (S_ISDIR(mode) || S_ISGITLINK(mode)) - return htonl(S_IFGITLINK); - return htonl(S_IFREG | ce_permissions(mode)); + return S_IFGITLINK; + return S_IFREG | ce_permissions(mode); } static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode) { extern int trust_executable_bit, has_symlinks; if (!has_symlinks && S_ISREG(mode) && - ce && S_ISLNK(ntohl(ce->ce_mode))) + ce && S_ISLNK(ce->ce_mode)) return ce->ce_mode; if (!trust_executable_bit && S_ISREG(mode)) { - if (ce && S_ISREG(ntohl(ce->ce_mode))) + if (ce && S_ISREG(ce->ce_mode)) return ce->ce_mode; return create_ce_mode(0666); } return create_ce_mode(mode); } +static inline int ce_to_dtype(const struct cache_entry *ce) +{ + unsigned ce_mode = ntohl(ce->ce_mode); + if (S_ISREG(ce_mode)) + return DT_REG; + else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode)) + return DT_DIR; + else if (S_ISLNK(ce_mode)) + return DT_LNK; + else + return DT_UNKNOWN; +} #define canon_mode(mode) \ (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \ S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK) #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7) +#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7) struct index_state { struct cache_entry **cache; unsigned int cache_nr, cache_alloc, cache_changed; struct cache_tree *cache_tree; time_t timestamp; - void *mmap; - size_t mmap_size; + void *alloc; + unsigned name_hash_initialized : 1; + struct hash_table name_hash; }; extern struct index_state the_index; @@@ -228,7 -177,6 +228,7 @@@ #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) +#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen)) #endif enum object_type { @@@ -315,7 -263,6 +315,7 @@@ extern int read_index_from(struct index extern int write_index(struct index_state *, int newfd); extern int discard_index(struct index_state *); extern int verify_path(const char *path); +extern int index_name_exists(struct index_state *istate, const char *name, int namelen); extern int index_name_pos(struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ @@@ -383,6 -330,14 +383,14 @@@ extern size_t packed_git_limit extern size_t delta_base_cache_limit; extern int auto_crlf; + enum safe_crlf { + SAFE_CRLF_FALSE = 0, + SAFE_CRLF_FAIL = 1, + SAFE_CRLF_WARN = 2, + }; + + extern enum safe_crlf safe_crlf; + #define GIT_REPO_VERSION 0 extern int repository_format_version; extern int check_repository_format(void); @@@ -637,16 -592,11 +645,16 @@@ extern int git_parse_ulong(const char * extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); extern int git_config_bool(const char *, const char *); +extern int git_config_string(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value); +extern int git_env_bool(const char *, int); +extern int git_config_system(void); +extern int git_config_global(void); +extern int config_error_nonbool(const char *); #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; @@@ -666,12 -616,12 +674,12 @@@ extern int write_or_whine_pipe(int fd, /* pager.c */ extern void setup_pager(void); -extern char *pager_program; +extern const char *pager_program; extern int pager_in_use(void); extern int pager_use_color; -extern char *editor_program; -extern char *excludes_file; +extern const char *editor_program; +extern const char *excludes_file; /* base85 */ int decode_85(char *dst, const char *line, int linelen); @@@ -691,7 -641,8 +699,8 @@@ extern void trace_argv_printf(const cha /* convert.c */ /* returns 1 if *dst was used */ - extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst); + extern int convert_to_git(const char *path, const char *src, size_t len, + struct strbuf *dst, enum safe_crlf checksafe); extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* add */ diff --combined config.c index 7881bbc778,3256c99553..8064cae182 --- a/config.c +++ b/config.c @@@ -309,14 -309,6 +309,14 @@@ int git_config_bool(const char *name, c return git_config_int(name, value) != 0; } +int git_config_string(const char **dest, const char *var, const char *value) +{ + if (!value) + return config_error_nonbool(var); + *dest = xstrdup(value); + return 0; +} + int git_default_config(const char *var, const char *value) { /* This needs a better name */ @@@ -415,43 -407,59 +415,52 @@@ return 0; } + if (!strcmp(var, "core.safecrlf")) { + if (value && !strcasecmp(value, "warn")) { + safe_crlf = SAFE_CRLF_WARN; + return 0; + } + safe_crlf = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "user.name")) { + if (!value) + return config_error_nonbool(var); strlcpy(git_default_name, value, sizeof(git_default_name)); return 0; } if (!strcmp(var, "user.email")) { + if (!value) + return config_error_nonbool(var); strlcpy(git_default_email, value, sizeof(git_default_email)); return 0; } - if (!strcmp(var, "i18n.commitencoding")) { - git_commit_encoding = xstrdup(value); - return 0; - } - - if (!strcmp(var, "i18n.logoutputencoding")) { - git_log_output_encoding = xstrdup(value); - return 0; - } + if (!strcmp(var, "i18n.commitencoding")) + return git_config_string(&git_commit_encoding, var, value); + if (!strcmp(var, "i18n.logoutputencoding")) + return git_config_string(&git_log_output_encoding, var, value); if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { pager_use_color = git_config_bool(var,value); return 0; } - if (!strcmp(var, "core.pager")) { - pager_program = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.pager")) + return git_config_string(&pager_program, var, value); - if (!strcmp(var, "core.editor")) { - editor_program = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.editor")) + return git_config_string(&editor_program, var, value); - if (!strcmp(var, "core.excludesfile")) { - if (!value) - die("core.excludesfile without value"); - excludes_file = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.excludesfile")) + return git_config_string(&excludes_file, var, value); if (!strcmp(var, "core.whitespace")) { + if (!value) + return config_error_nonbool(var); whitespace_rule_cfg = parse_whitespace_rule(value); return 0; } @@@ -493,22 -501,6 +502,22 @@@ const char *git_etc_gitconfig(void return system_wide; } +int git_env_bool(const char *k, int def) +{ + const char *v = getenv(k); + return v ? git_config_bool(k, v) : def; +} + +int git_config_system(void) +{ + return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); +} + +int git_config_global(void) +{ + return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); +} + int git_config(config_fn_t fn) { int ret = 0; @@@ -521,7 -513,7 +530,7 @@@ * config file otherwise. */ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { - if (!access(git_etc_gitconfig(), R_OK)) + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) ret += git_config_from_file(fn, git_etc_gitconfig()); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); @@@ -529,7 -521,7 +538,7 @@@ filename = repo_config = xstrdup(git_path("config")); } - if (home) { + if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) ret = git_config_from_file(fn, user_config); @@@ -718,17 -710,12 +727,17 @@@ static ssize_t find_beginning_of_line(c size_t equal_offset = size, bracket_offset = size; ssize_t offset; +contline: for (offset = offset_-2; offset > 0 && contents[offset] != '\n'; offset--) switch (contents[offset]) { case '=': equal_offset = offset; break; case ']': bracket_offset = offset; break; } + if (offset > 0 && contents[offset-1] == '\\') { + offset_ = offset; + goto contline; + } if (bracket_offset < equal_offset) { *found_bracket = 1; offset = bracket_offset+1; @@@ -1096,12 -1083,3 +1105,12 @@@ int git_config_rename_section(const cha free(config_filename); return ret; } + +/* + * Call this to report error for your variable that should not + * get a boolean value (i.e. "[my] var" means "true"). + */ +int config_error_nonbool(const char *var) +{ + return error("Missing value for '%s'", var); +} diff --combined convert.c index 552707e8e6,3bbf0c51c2..d8c94cb3ed --- a/convert.c +++ b/convert.c @@@ -85,8 -85,39 +85,39 @@@ static int is_binary(unsigned long size return 0; } + static void check_safe_crlf(const char *path, int action, + struct text_stat *stats, enum safe_crlf checksafe) + { + if (!checksafe) + return; + + if (action == CRLF_INPUT || auto_crlf <= 0) { + /* + * 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); + else /* i.e. SAFE_CRLF_FAIL */ + die("CRLF would be replaced by LF in %s.", path); + } + } else if (auto_crlf > 0) { + /* + * 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); + else /* i.e. SAFE_CRLF_FAIL */ + die("LF would be replaced by CRLF in %s", path); + } + } + } + static int crlf_to_git(const char *path, const char *src, size_t len, - struct strbuf *buf, int action) + struct strbuf *buf, int action, enum safe_crlf checksafe) { struct text_stat stats; char *dst; @@@ -95,9 -126,6 +126,6 @@@ return 0; gather_stats(src, len, &stats); - /* No CR? Nothing to convert, regardless. */ - if (!stats.cr) - return 0; if (action == CRLF_GUESS) { /* @@@ -115,6 -143,12 +143,12 @@@ return 0; } + check_safe_crlf(path, action, &stats, checksafe); + + /* Optimization: No CR? Nothing to convert, regardless. */ + if (!stats.cr) + return 0; + /* only grow if not in place */ if (strbuf_avail(buf) + buf->len < len) strbuf_grow(buf, len - buf->len); @@@ -326,14 -360,14 +360,14 @@@ static int read_convert_config(const ch if (!strcmp("smudge", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); drv->smudge = strdup(value); return 0; } if (!strcmp("clean", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); drv->clean = strdup(value); return 0; } @@@ -536,7 -570,8 +570,8 @@@ static int git_path_check_ident(const c return !!ATTR_TRUE(value); } - int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst) + 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[3]; int crlf = CRLF_GUESS; @@@ -558,7 -593,7 +593,7 @@@ src = dst->buf; len = dst->len; } - ret |= crlf_to_git(path, src, len, dst, crlf); + ret |= crlf_to_git(path, src, len, dst, crlf, checksafe); if (ret) { src = dst->buf; len = dst->len; diff --combined diff.c index 41ec2ced78,562c20ed40..58fe7750f9 --- a/diff.c +++ b/diff.c @@@ -57,7 -57,7 +57,7 @@@ static int parse_diff_color_slot(const static struct ll_diff_driver { const char *name; struct ll_diff_driver *next; - char *cmd; + const char *cmd; } *user_diff, **user_diff_tail; /* @@@ -86,7 -86,10 +86,7 @@@ static int parse_lldiff_command(const c user_diff_tail = &(drv->next); } - if (!value) - return error("%s: lacks value", var); - drv->cmd = strdup(value); - return 0; + return git_config_string(&(drv->cmd), var, value); } /* @@@ -155,16 -158,16 +155,16 @@@ int git_diff_ui_config(const char *var return 0; } if (!strcmp(var, "diff.external")) { + if (!value) + return config_error_nonbool(var); external_diff_cmd_cfg = xstrdup(value); return 0; } if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); - if (ep != var + 4) { - if (!strcmp(ep, ".command")) - return parse_lldiff_command(var, ep, value); - } + if (ep != var + 4 && !strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); } return git_diff_basic_config(var, value); @@@ -174,8 -177,6 +174,8 @@@ int git_diff_basic_config(const char *v { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); + if (!value) + return config_error_nonbool(var); color_parse(value, var, diff_colors[slot]); return 0; } @@@ -183,11 -184,8 +183,11 @@@ if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { - if (!strcmp(ep, ".funcname")) + if (!strcmp(ep, ".funcname")) { + if (!value) + return config_error_nonbool(var); return parse_funcname_pattern(var, ep, value); + } } } @@@ -1013,7 -1011,6 +1013,7 @@@ static void checkdiff_consume(void *pri char *err; if (line[0] == '+') { + data->lineno++; data->status = check_and_emit_line(line + 1, len - 1, data->ws_rule, NULL, NULL, NULL, NULL); if (!data->status) @@@ -1024,12 -1021,13 +1024,12 @@@ emit_line(set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, stdout, set, reset, ws); - data->lineno++; } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) - data->lineno = strtol(plus, NULL, 10); + data->lineno = strtol(plus, NULL, 10) - 1; else die("invalid diff"); } @@@ -1512,22 -1510,17 +1512,22 @@@ static int reuse_worktree_file(const ch if (pos < 0) return 0; ce = active_cache[pos]; - if ((lstat(name, &st) < 0) || - !S_ISREG(st.st_mode) || /* careful! */ - ce_match_stat(ce, &st, 0) || - hashcmp(sha1, ce->sha1)) + + /* + * This is not the sha1 we are looking for, or + * unreusable because it is not a regular file. + */ + if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode)) return 0; - /* we return 1 only when we can stat, it is a regular file, - * stat information matches, and sha1 recorded in the cache - * matches. I.e. we know the file in the work tree really is - * the same as the pair. + + /* + * If ce matches the file in the work tree, we can reuse it. */ - return 1; + if (ce_uptodate(ce) || + (!lstat(name, &st) && !ce_match_stat(ce, &st, 0))) + return 1; + + return 0; } static int populate_from_stdin(struct diff_filespec *s) @@@ -1631,7 -1624,7 +1631,7 @@@ int diff_populate_filespec(struct diff_ * Convert from working tree format to canonical git format */ strbuf_init(&buf, 0); - if (convert_to_git(s->path, s->data, s->size, &buf)) { + if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; diff --combined environment.c index fa3633372b,e351e99ed7..3527f1663f --- a/environment.c +++ b/environment.c @@@ -30,11 -30,12 +30,12 @@@ int core_compression_seen size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 16 * 1024 * 1024; -char *pager_program; +const char *pager_program; int pager_use_color = 1; -char *editor_program; -char *excludes_file; +const char *editor_program; +const char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ + enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; /* This is set by setup_git_dir_gently() and/or git_default_config() */