ls-files: Add eol diagnostics
PATCH v3 1/1 ls-files: Add eol diagnostics
When working in a cross-platform environment, a user wants to check if text files are stored normalized in the repository and if .gitattributes are set appropriately.
Make it possible to let Git show the line endings in the index and in the working tree and the effective text/eol attributes
The end of line ("eolinfo") are shown like this:
"binary" binary file "text-no-eol" text file without any EOL "text-lf" text file with LF "text-crlf" text file with CRLF
"text-crlf-lf" text file with mixed line endings.
The effective text/eol attribute is one of these:
"", "-text", "text", "text=auto", "eol=lf", "eol=crlf"
git ls-files --eol gives an output like this:
i/text-no-eol w/text-no-eol attr/text=auto t/t5100/empty i/binary w/binary attr/-text t/test-binary-2.png i/text-lf w/text-lf attr/eol=lf t/t5100/rfc2047-info-0007 i/text-lf w/text-crlf attr/eol=crlf doit.bat i/text-crlf-lf w/text-crlf-lf attr/ locale/XX.po
Note that the output is meant to be human-readable and may change.
---
Changes since v2:
- Major rework
- New prefix, more common to what we use in Git (i/, w/ attr/)
- Only one option: git ls-files --eol (may be combined with -s or -o or -d
- Simplify the diagnostics: either the file is binary or text As empty files are not binary, they are text-no-eol
- Test cases in t0027
- The documentation is improved, but may need more improving
Documentation/git-ls-files.txt | 22 +++++++++ builtin/check-attr.c | 34 ++++++++++++-- builtin/ls-files.c | 19 ++++++++ convert.c | 86 ++++++++++++++++++++++++++++++++++ convert.h | 3 ++ t/t0027-auto-crlf.sh | 104 ++++++++++++++++++++++++++++++++++++----- 6 files changed, 252 insertions(+), 16 deletions(-)
Documentation/git-ls-files.txt +22 -0
@@ -12,6 +12,7 @@ SYNOPSIS 'git ls-files' [-z] [-t] [-v] (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])* (-[c|d|o|i|s|u|k|m])* + [--eol] [-x <pattern>|--exclude=<pattern>] [-X <file>|--exclude-from=<file>] [--exclude-per-directory=<file>]
@@ -147,6 +148,18 @@ a space) at the start of each line: possible for manual inspection; the exact format may change at any time. +--eol:: + Show line endings ("eolinfo") and the text/eol attributes ("texteolattr") of files. + "eolinfo" is the file content identification used by Git when + the "text" attribute is "auto", or core.autocrlf != false. + + "eolinfo" is either "" (when the the info is not avaiable"), or one of "binary", + "text-no-eol", "text-lf", "text-crlf" or "text-crlf-lf". + The "texteolattr" can be "", "-text", "text", "text=auto", "eol=lf", "eol=crlf". + + Both the content in the index ("i/") and the content in the working tree ("w/") + are shown for regular files, followed by the "exteolattr ("attr/"). + \--:: Do not interpret any more arguments as options.
@@ -161,6 +174,15 @@ which case it outputs: [<tag> ]<mode> <object> <stage> <file> +'git ls-files --eol' will show + i/<eolinfo> w/<eolinfo> attr/<eolattr> <file> + +'git ls-files --eol -o' will show + i/ w/<eolinfo> attr/<eolattr> <file> + +'git ls-files --eol -s' will show +[<tag> ]<mode> <object> <stage> i/<eolinfo> w/<eolinfo> attr/<eolattr> <file> + 'git ls-files --unmerged' and 'git ls-files --stage' can be used to examine detailed information on unmerged paths.
builtin/check-attr.c +30 -4
@@ -5,6 +5,7 @@#include "parse-options.h" static int all_attrs; +static int eol_attrs;static int cached_attrs;static int stdin_paths;static const char *const check_attr_usage[] = {
@@ -17,6 +18,7 @@ static int nul_term_line;static const struct option check_attr_options[] = {OPT_BOOL ('a' ,"all" , &all_attrs,N_ ("report all attributes set on file" )), +OPT_BOOL ('e' ,"eol" , &eol_attrs,N_ ("report effective eol attributes set on file" )),OPT_BOOL (0 ,"cached" , &cached_attrs,N_ ("use .gitattributes only from the index" )),OPT_BOOL (0 ,"stdin" , &stdin_paths,N_ ("read file names from stdin" )), OPT_BOOL('z', NULL, &nul_term_line,
@@ -51,6 +53,23 @@ static void output_attr(int cnt, struct git_attr_check *check, } } +static void output_eol_attr (const char *file) +{ +struct stat st; +if (lstat (file, &st) || (!S_ISREG (st.st_mode))) +return ; +if (nul_term_line) { +printf ("%s:" , file); +if (!lstat (file, &st) && (S_ISREG (st.st_mode))) +printf (": %-13s " ,get_convert_attr_ascii (file)); +printf ("%c" ,0 ); + }else { +quote_c_style (file,NULL , stdout,0 ); +printf (": %-13s " ,get_convert_attr_ascii (file)); +printf ("\n" ); + } +} +static void check_attr (const char *prefix,int cnt,struct git_attr_check *check,const char *file) {
@@ -61,7 +80,9 @@ static void check_attr(const char *prefix, int cnt,die ("git_check_attr died" );output_attr (cnt, check, file); }else { -if (git_all_attrs (full_path, &cnt, &check)) +if (eol_attrs) +output_eol_attr (full_path); +else if (git_all_attrs (full_path, &cnt, &check))die ("git_all_attrs died" );output_attr (cnt, check, file);free (check);
@@ -123,8 +144,13 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) doubledash = i; } -/* Process --all and/or attribute arguments: */ -if (all_attrs) { +if (eol_attrs) { +if (doubledash >=1 ) +error_with_usage ("Attributes and --eol both specified" ); + cnt =0 ; + filei = doubledash +1 ; + }else if (all_attrs) { +/* Process --all and/or attribute arguments: */ if (doubledash >=1 )error_with_usage ("Attributes and --all both specified" );
@@ -159,7 +185,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)error_with_usage ("No file specified" ); } -if (all_attrs) { +if (all_attrs || eol_attrs) { check =NULL ; }else { check =xcalloc (cnt,sizeof (*check));
builtin/ls-files.c +19 -0
@@ -27,6 +27,7 @@ static int show_killed;static int show_valid_bit;static int line_terminator ='\n' ;static int debug_mode; +static int show_eol;static const char *prefix;static int max_prefix_len;
@@ -47,6 +48,21 @@ static const char *tag_modified = "";static const char *tag_skip_worktree ="" ;static const char *tag_resolve_undo ="" ; +static void write_eolinfo (const struct cache_entry *ce,const char *path) +{ +struct stat st; +const char *i_txt ="" ; +const char *w_txt ="" ; +if (!show_eol) +return ; +if (ce &&S_ISREG (ce->ce_mode)) + i_txt =get_cached_convert_stats_ascii (ce->name); +if (!lstat (path, &st) && (S_ISREG (st.st_mode))) + w_txt =get_wt_convert_stats_ascii (path); +printf ("i/%-13s w/%-13s attr/%-9s " , i_txt, w_txt, +get_convert_attr_ascii (path)); +} +static void write_name (const char *name) {/*
@@ -68,6 +84,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)return ;fputs (tag, stdout); +write_eolinfo (NULL , ent->name);write_name (ent->name); }
@@ -170,6 +187,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)find_unique_abbrev (ce->sha1,abbrev), ce_stage(ce)); } +write_eolinfo (ce, ce->name);write_name (ce->name);if (debug_mode) {const struct stat_data *sd = &ce->ce_stat_data;
@@ -433,6 +451,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)OPT_BIT (0 ,"directory" , &dir.flags,N_ ("show 'other' directories' names only" ),DIR_SHOW_OTHER_DIRECTORIES ), +OPT_BOOL (0 ,"eol" , &show_eol,N_ ("show line endings of files" )),OPT_NEGBIT (0 ,"empty-directory" , &dir.flags,N_ ("don't show empty directories" ), DIR_HIDE_EMPTY_DIRECTORIES),
convert.c +86 -0
104 lines not on this page · raw message
convert.h +3 -0
@@ -32,6 +32,9 @@ enum eol { };extern enum eol core_eol; +extern const char *get_cached_convert_stats_ascii (const char *path); +extern const char *get_wt_convert_stats_ascii (const char *path); +extern const char *get_convert_attr_ascii (const char *path);/* returns 1 if *dst was used */ extern int convert_to_git(const char *path, const char *src, size_t len,
t/t0027-auto-crlf.sh +92 -12
143 lines not on this page · raw message
Signature · 1 line
2.6.1.443.g36d7748
Quoted 3 lines · Torsten Bögershausen
git ls-files --eol gives an output like this:
i/text-no-eol w/text-no-eol attr/text=auto t/t5100/empty
I'm sorry if this has been discussed before, but hav you considered to use a header line and omit the prefixed from the columns instead? Like
index working tree attributes file
binary binary -text t/test-binary-2.png text-lf text-lf eol=lf t/t5100/rfc2047-info-0007 text-lf text-crlf eol=crlf doit.bat text-crlf-lf text-crlf-lf locale/XX.po
I believe this would be both easier to read for humans, and easier to parse for scripts that e.g. want to compare line endings in the index and working tree.
Quoted 2 lines
t/t0027-auto-crlf.sh +2
-0+stats_ascii () { + case "$1" in
[...]
t/t0027-auto-crlf.sh commit_chk_wrnNNO () { · line 172
Quoted 3 lines
t/t0027-auto-crlf.sh +3
-0+ *) + echo huh $1 + ;;
Personally, I'm not a big fan of supposedly funny output like this. How about printing a proper message rather than "huh", even for cases that should not happen?
Signature · 1 line
Sebastian Schuberth
Quoted 8 lines · Sebastian Schuberth
Quoted 3 lines · Torsten Bögershausen
git ls-files --eol gives an output like this:
i/text-no-eol w/text-no-eol attr/text=auto t/t5100/empty
I'm sorry if this has been discussed before, but hav you considered to use a header line and omit the prefixed from the columns instead? Like
Quoted 9 lines
index working tree attributes file
binary binary -text t/test-binary-2.png text-lf text-lf eol=lf t/t5100/rfc2047-info-0007 text-lf text-crlf eol=crlf doit.bat text-crlf-lf text-crlf-lf locale/XX.poI believe this would be both easier to read for humans, and easier to parse for scripts that e.g. want to compare line endings in the index and working tree.
The problem I see is to make sure that there is always a separator, even when a field empty:
rm zlib.c; git ls-file --eol #will include a line like this:
i/text-lf w/ attr/ zlib.c
or, as another example:
git ls-files -o --eol
i/ w/binary attr/ zlib.o
And if there is no separator, it is harder to make it machine-parsable, if we e.g. extend the attributes to support "*text=autocrlf", or "*.text=autoinput" (But that is another story)
If we replace "/[-a-z]" with "\t", the line has always a separator, but needs a somewhat wider screen:
text-lf text-lf zlib.c
Quoted 1 line
t/t0027-auto-crlf.sh commit_chk_wrnNNO () { · line 171
Quoted 1 line
t/t0027-auto-crlf.sh +1
-0+ echo huh $1
[] good catch, thanks
Quoted 18 lines · Torsten Bögershausen
Quoted 8 lines · Sebastian Schuberth
Quoted 3 lines · Torsten Bögershausen
git ls-files --eol gives an output like this:
i/text-no-eol w/text-no-eol attr/text=auto t/t5100/empty
I'm sorry if this has been discussed before, but hav you considered to use a header line and omit the prefixed from the columns instead? Like
Quoted 9 lines
index working tree attributes file
binary binary -text t/test-binary-2.png text-lf text-lf eol=lf t/t5100/rfc2047-info-0007 text-lf text-crlf eol=crlf doit.bat text-crlf-lf text-crlf-lf locale/XX.poI believe this would be both easier to read for humans, and easier to parse for scripts that e.g. want to compare line endings in the index and working tree.
Quoted 9 lines
The problem I see is to make sure that there is always a separator, even when a field empty:
rm
zlib.c; git ls-file --eol #will include a line like this:
i/text-lf w/ attr/zlib.cor, as another example:
git ls-files -o --eol
i/ w/binary attr/ zlib.o
[I have been trying *not* to respond to this thread, since the following comment is classic bikeshedding on the naming of things and I am, in general, hopeless at naming things ... :-D ]
<bikeshedding>
I don't think you need to include 'text' in the name of the line ending descriptors - if its not binary then its some form of text. So, maybe something like:
binary
none
lf
crlf
mixed
</bikeshedding>
HTH
ATB,
Ramsay Jones
Quoted 3 lines · Torsten Bögershausen
or, as another example:
git ls-files -o --eol
i/ w/binary attr/ zlib.o
I see, somewhat convincing I have to agree.
On another note, how about making the prefix either all just one letter (i.e. "attr/" becomes "a/"), or all multi-letter abbreviations (i.e. "i/" becomes "idx/" and "w/" becomes "wt/" or "wtree/" or "tree/")?
Signature · 1 line
Sebastian Schuberth
Quoted 10 lines · Sebastian Schuberth
Quoted 2 lines · Torsten Bögershausen
git ls-files -o --eol
i/ w/binary attr/ zlib.oI see, somewhat convincing I have to agree.
On another note, how about making the prefix either all just one letter (i.e. "attr/" becomes "a/"), or all multi-letter abbreviations (i.e. "i/" becomes "idx/" and "w/" becomes "wt/" or "wtree/" or "tree/")?
The latter is answered by [1].
[1]: http://article.gmane.org/gmane.comp.version-control.git/280647
Quoted 6 lines · Torsten Bögershausen
When working in a cross-platform environment, a user wants to check if text files are stored normalized in the repository and if .gitattributes are set appropriately.
Make it possible to let Git show the line endings in the index and in the working tree and the effective text/eol attributes
s/$/./
Quoted 5 lines
[...]Signed-off-by Torsten Bögershausen <[email protected]>---
Documentation/git-ls-files.txt a space) at the start of each line: · line 156
Quoted 9 lines
Documentation/git-ls-files.txt +6
-0@@ -147,6 +148,18 @@ a space) at the start of each line: possible for manual inspection; the exact format may change at any time. +--eol:: + Show line endings ("eolinfo") and the text/eol attributes ("texteolattr") of files. + "eolinfo" is the file content identification used by Git when + the "text" attribute is "auto", or core.autocrlf != false. + + "eolinfo" is either "" (when the the info is not avaiable"), or one of "binary",
s/avaiable/available/
Documentation/git-ls-files.txt a space) at the start of each line: · line 161
Quoted 5 lines
Documentation/git-ls-files.txt +5
-0+ "text-no-eol", "text-lf", "text-crlf" or "text-crlf-lf". + The "texteolattr" can be "", "-text", "text", "text=auto", "eol=lf", "eol=crlf". + + Both the content in the index ("i/") and the content in the working tree ("w/") + are shown for regular files, followed by the "exteolattr ("attr/").
s/exteolattr/texteolattr/
Should the documentation mention that with --eol ls-files becomes a much more expensive operation since it now has to read all blobs referenced by trees? (genuine question)
builtin/check-attr.c static void output_attr(int cnt, struct git_attr_check *check, · line 61
Quoted 11 lines
builtin/check-attr.c +9
-0@@ -51,6 +53,23 @@ static void output_attr(int cnt, struct git_attr_check *check, +static void output_eol_attr (const char *file) +{ +struct stat st; +if (lstat (file, &st) || (!S_ISREG (st.st_mode))) +return ; +if (nul_term_line) { +printf ("%s:" , file); +if (!lstat (file, &st) && (S_ISREG (st.st_mode))) +printf (": %-13s " ,get_convert_attr_ascii (file));
Why the trailing space in the format string?
builtin/check-attr.c static void output_attr(int cnt, struct git_attr_check *check, · line 65
Quoted 1 line
builtin/check-attr.c +1
-0+printf ("%c" ,0 );
putc('\0') perhaps?
builtin/check-attr.c static void output_attr(int cnt, struct git_attr_check *check, · line 68
Quoted 3 lines
builtin/check-attr.c +3
-0+ }else { +quote_c_style (file,NULL , stdout,0 ); +printf (": %-13s " ,get_convert_attr_ascii (file));
Ditto regarding trailing space.
builtin/check-attr.c static void output_attr(int cnt, struct git_attr_check *check, · line 69
Quoted 1 line
builtin/check-attr.c +1
-0+printf ("\n" );
Is there a reason the "\n" isn't incorporated into the preceding printf's format string?
Quoted 5 lines
builtin/check-attr.c +2
-0+ } +}t/t0027-auto-crlf.sh +0
-0@@ -213,7 +238,18 @@ checkout_files () { git -c core.eol=$eol checkout $src$f.txt
t/t0027-auto-crlf.sh checkout_files () { · line 247
Quoted 10 lines
t/t0027-auto-crlf.sh +7
-1fi done - + test_expect_success "ls-files --eol $lfname ${pfx}LF.txt" " + echo i/text-lf w/$(stats_ascii $lfname) ${src}LF.txt >e && + echo i/text-crlf w/$(stats_ascii $crlfname) ${src}CRLF.txt >>e && + echo i/text-crlf-lf w/$(stats_ascii $lfmixcrlf) ${src}CRLF_mix_LF.txt >>e && + echo i/binary w/$(stats_ascii $lfmixcr) ${src}LF_mix_CR.txt >>e && + echo i/binary w/$(stats_ascii $crlfnul) ${src}CRLF_nul.txt >>e && + echo i/binary w/$(stats_ascii $crlfnul) ${src}LF_nul.txt >>e &&
I wonder if a here-doc would make this a bit more readable and maintainable...
echo >e <<-EOF &&
i/text-lf w/$(stats_ascii $lfname) ${src}LF.txt
i/text-crlf w/$(stats_ascii $crlfname) ${src}CRLF.txt
i/text-crlf-lf w/$(stats_ascii $lfmixcrlf) ${src}CRLF_mix_LF.txt
i/binary w/$(stats_ascii $lfmixcr) ${src}LF_mix_CR.txt
i/binary w/$(stats_ascii $crlfnul) ${src}CRLF_nul.txt
i/binary w/$(stats_ascii $crlfnul) ${src}LF_nul.txt
EOFQuoted 23 lines
t/t0027-auto-crlf.sh +19
-0+ sort <e >exp && + git ls-files --eol $src* | sed -e 's!attr/[=a-z-]*!!g' -e 's/ */ /g' | sort >act && + test_cmp exp act && + rm e exp act + "@@ -231,6 +267,35 @@ checkout_files () { " } +# Test control characters +# NUL SOH CR EOF==^Z +test_expect_success 'ls-files --eol -o Text/Binary' ' + test_when_finished "rm e exp act TeBi_*" && + STRT=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA && + STR=$STRT$STRT$STRT$STRT && + printf "${STR}BBB\001" >TeBi_127_S && + printf "${STR}BBBB\001">TeBi_128_S && + printf "${STR}BBB\032" >TeBi_127_E && + printf "\032${STR}BBB" >TeBi_E_127 && + printf "${STR}BBBB\000">TeBi_128_N && + printf "${STR}BBB\012">TeBi_128_L && + printf "${STR}BBB\015">TeBi_127_C && + printf "${STR}BB\015\012" >TeBi_126_CL &&
t/t0027-auto-crlf.sh checkout_files () { · line 293
Quoted 10 lines
t/t0027-auto-crlf.sh +10
-0+ printf "${STR}BB\015\012\015" >TeBi_126_CLC && + echo i/ w/binary TeBi_127_S >e && + echo i/ w/text-no-eol TeBi_128_S >>e && + echo i/ w/text-no-eol TeBi_127_E >>e && + echo i/ w/binary TeBi_E_127 >>e && + echo i/ w/binary TeBi_128_N >>e && + echo i/ w/text-lf TeBi_128_L >>e && + echo i/ w/binary TeBi_127_C >>e && + echo i/ w/text-crlf TeBi_126_CL >>e && + echo i/ w/binary TeBi_126_CLC >>e &&
here-doc equivalent:
echo >e <<-\EOF &&
i/ w/binary TeBi_127_S
i/ w/text-no-eol TeBi_128_S
i/ w/text-no-eol TeBi_127_E
i/ w/binary TeBi_E_127
i/ w/binary TeBi_128_N
i/ w/text-lf TeBi_128_L
i/ w/binary TeBi_127_C
i/ w/text-crlf TeBi_126_CL
i/ w/binary TeBi_126_CLC
EOFt/t0027-auto-crlf.sh checkout_files native true "lf" LF CRLF CRLF_mix_LF LF_mix_CR · line 551
Quoted 13 lines
t/t0027-auto-crlf.sh +12
-0+ sort <e >exp && + git ls-files --eol -o | egrep "TeBi_" | sed -e 's!attr/[=a-z-]*!!g' -e "s/ */ /g" | sort >act && + test_cmp exp act +' +@@ -480,4 +545,19 @@ checkout_files native true "lf" LF CRLF CRLF_mix_LF LF_mix_CR +# Should be the last test case +test_expect_success 'ls-files --eol -d' " + rm crlf_false_attr__CRLF.txt crlf_false_attr__CRLF_mix_LF.txt crlf_false_attr__LF.txt .gitattributes && + echo i/text-lf w/ .gitattributes >e && + echo i/text-crlf w/ crlf_false_attr__CRLF.txt >>e && + echo i/text-crlf-lf w/ crlf_false_attr__CRLF_mix_LF.txt >>e && + echo i/text-lf w/ crlf_false_attr__LF.txt >>e &&
echo >e <<-\EOF &&
i/text-lf w/ .gitattributes
i/text-crlf w/ crlf_false_attr__CRLF.txt i/text-crlf-lf w/ crlf_false_attr__CRLF_mix_LF.txt i/text-lf w/ crlf_false_attr__LF.txt
EOF
Quoted 10 lines
t/t0027-auto-crlf.sh +7
-1+ sort <e >exp && + git ls-files --eol -d | sed -e 's!attr/[=a-z-]*!!g' -e 's/ */ /g' | sort >act && + test_cmp exp act && + rm e exp act +" + + test_done --2.6.1.443.g36d7748