Skip to content

Commit 9472935

Browse files
tboegigitster
authored andcommitted
add: introduce "--renormalize"
Make it safer to normalize the line endings in a repository. Files that had been commited with CRLF will be commited with LF. The old way to normalize a repo was like this: # Make sure that there are not untracked files $ echo "* text=auto" >.gitattributes $ git read-tree --empty $ git add . $ git commit -m "Introduce end-of-line normalization" The user must make sure that there are no untracked files, otherwise they would have been added and tracked from now on. The new "add --renormalize" does not add untracked files: $ echo "* text=auto" >.gitattributes $ git add --renormalize . $ git commit -m "Introduce end-of-line normalization" Note that "git add --renormalize <pathspec>" is the short form for "git add -u --renormalize <pathspec>". While at it, document that the same renormalization may be needed, whenever a clean filter is added or changed. Helped-By: Junio C Hamano <[email protected]> Signed-off-by: Torsten Bögershausen <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cb5918a commit 9472935

File tree

7 files changed

+102
-18
lines changed

7 files changed

+102
-18
lines changed

Documentation/git-add.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ SYNOPSIS
1010
[verse]
1111
'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
1212
[--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
13-
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing]
13+
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
1414
[--chmod=(+|-)x] [--] [<pathspec>...]
1515

1616
DESCRIPTION
@@ -175,6 +175,13 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
175175
warning (e.g., if you are manually performing operations on
176176
submodules).
177177

178+
--renormalize::
179+
Apply the "clean" process freshly to all tracked files to
180+
forcibly add them again to the index. This is useful after
181+
changing `core.autocrlf` configuration or the `text` attribute
182+
in order to correct files added with wrong CRLF/LF line endings.
183+
This option implies `-u`.
184+
178185
--chmod=(+|-)x::
179186
Override the executable bit of the added files. The executable
180187
bit is only changed in the index, the files on disk are left

Documentation/gitattributes.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,7 @@ From a clean working directory:
232232

233233
-------------------------------------------------
234234
$ echo "* text=auto" >.gitattributes
235-
$ git read-tree --empty # Clean index, force re-scan of working directory
236-
$ git add .
235+
$ git add --renormalize .
237236
$ git status # Show files that will be normalized
238237
$ git commit -m "Introduce end-of-line normalization"
239238
-------------------------------------------------
@@ -328,6 +327,9 @@ You can declare that a filter turns a content that by itself is unusable
328327
into a usable content by setting the filter.<driver>.required configuration
329328
variable to `true`.
330329

330+
Note: Whenever the clean filter is changed, the repo should be renormalized:
331+
$ git add --renormalize .
332+
331333
For example, in .gitattributes, you would assign the `filter`
332334
attribute for paths.
333335

builtin/add.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static const char * const builtin_add_usage[] = {
2626
};
2727
static int patch_interactive, add_interactive, edit_interactive;
2828
static int take_worktree_changes;
29+
static int add_renormalize;
2930

3031
struct update_callback_data {
3132
int flags;
@@ -123,6 +124,25 @@ int add_files_to_cache(const char *prefix,
123124
return !!data.add_errors;
124125
}
125126

127+
static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
128+
{
129+
int i, retval = 0;
130+
131+
for (i = 0; i < active_nr; i++) {
132+
struct cache_entry *ce = active_cache[i];
133+
134+
if (ce_stage(ce))
135+
continue; /* do not touch unmerged paths */
136+
if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
137+
continue; /* do not touch non blobs */
138+
if (pathspec && !ce_path_match(ce, pathspec, NULL))
139+
continue;
140+
retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE);
141+
}
142+
143+
return retval;
144+
}
145+
126146
static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
127147
{
128148
char *seen;
@@ -276,6 +296,7 @@ static struct option builtin_add_options[] = {
276296
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
277297
OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
278298
OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
299+
OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")),
279300
OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
280301
OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
281302
{ OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
@@ -406,7 +427,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
406427
chmod_arg[1] != 'x' || chmod_arg[2]))
407428
die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
408429

409-
add_new_files = !take_worktree_changes && !refresh_only;
430+
add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
410431
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
411432

412433
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
@@ -500,7 +521,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
500521

501522
plug_bulk_checkin();
502523

503-
exit_status |= add_files_to_cache(prefix, &pathspec, flags);
524+
if (add_renormalize)
525+
exit_status |= renormalize_tracked_files(&pathspec, flags);
526+
else
527+
exit_status |= add_files_to_cache(prefix, &pathspec, flags);
504528

505529
if (add_new_files)
506530
exit_status |= add_files(&dir, flags);

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ extern int ie_modified(const struct index_state *, const struct cache_entry *, s
686686

687687
#define HASH_WRITE_OBJECT 1
688688
#define HASH_FORMAT_CHECK 2
689+
#define HASH_RENORMALIZE 4
689690
extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
690691
extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
691692

read-cache.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,17 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
631631
{
632632
int size, namelen, was_same;
633633
mode_t st_mode = st->st_mode;
634-
struct cache_entry *ce, *alias;
634+
struct cache_entry *ce, *alias = NULL;
635635
unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
636636
int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
637637
int pretend = flags & ADD_CACHE_PRETEND;
638638
int intent_only = flags & ADD_CACHE_INTENT;
639639
int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
640640
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
641+
int newflags = HASH_WRITE_OBJECT;
642+
643+
if (flags & HASH_RENORMALIZE)
644+
newflags |= HASH_RENORMALIZE;
641645

642646
if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
643647
return error("%s: can only add regular files, symbolic links or git-directories", path);
@@ -678,19 +682,23 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
678682
if (ignore_case) {
679683
adjust_dirname_case(istate, ce->name);
680684
}
685+
if (!(flags & HASH_RENORMALIZE)) {
686+
alias = index_file_exists(istate, ce->name,
687+
ce_namelen(ce), ignore_case);
688+
if (alias &&
689+
!ce_stage(alias) &&
690+
!ie_match_stat(istate, alias, st, ce_option)) {
691+
/* Nothing changed, really */
692+
if (!S_ISGITLINK(alias->ce_mode))
693+
ce_mark_uptodate(alias);
694+
alias->ce_flags |= CE_ADDED;
681695

682-
alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
683-
if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
684-
/* Nothing changed, really */
685-
if (!S_ISGITLINK(alias->ce_mode))
686-
ce_mark_uptodate(alias);
687-
alias->ce_flags |= CE_ADDED;
688-
689-
free(ce);
690-
return 0;
696+
free(ce);
697+
return 0;
698+
}
691699
}
692700
if (!intent_only) {
693-
if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
701+
if (index_path(&ce->oid, path, st, newflags)) {
694702
free(ce);
695703
return error("unable to index file %s", path);
696704
}

sha1_file.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ static struct cached_object *find_cached_object(const unsigned char *sha1)
7474
return NULL;
7575
}
7676

77+
78+
static enum safe_crlf get_safe_crlf(unsigned flags)
79+
{
80+
if (flags & HASH_RENORMALIZE)
81+
return SAFE_CRLF_RENORMALIZE;
82+
else if (flags & HASH_WRITE_OBJECT)
83+
return safe_crlf;
84+
else
85+
return SAFE_CRLF_FALSE;
86+
}
87+
88+
7789
int mkdir_in_gitdir(const char *path)
7890
{
7991
if (mkdir(path, 0777)) {
@@ -1680,7 +1692,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
16801692
if ((type == OBJ_BLOB) && path) {
16811693
struct strbuf nbuf = STRBUF_INIT;
16821694
if (convert_to_git(&the_index, path, buf, size, &nbuf,
1683-
write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
1695+
get_safe_crlf(flags))) {
16841696
buf = strbuf_detach(&nbuf, &size);
16851697
re_allocated = 1;
16861698
}
@@ -1714,7 +1726,7 @@ static int index_stream_convert_blob(unsigned char *sha1, int fd,
17141726
assert(would_convert_to_git_filter_fd(path));
17151727

17161728
convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
1717-
write_object ? safe_crlf : SAFE_CRLF_FALSE);
1729+
get_safe_crlf(flags));
17181730

17191731
if (write_object)
17201732
ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),

t/t0025-crlf-renormalize.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/sh
2+
3+
test_description='CRLF renormalization'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success setup '
8+
git config core.autocrlf false &&
9+
printf "LINEONE\nLINETWO\nLINETHREE\n" >LF.txt &&
10+
printf "LINEONE\r\nLINETWO\r\nLINETHREE\r\n" >CRLF.txt &&
11+
printf "LINEONE\r\nLINETWO\nLINETHREE\n" >CRLF_mix_LF.txt &&
12+
git add . &&
13+
git commit -m initial
14+
'
15+
16+
test_expect_success 'renormalize CRLF in repo' '
17+
echo "*.txt text=auto" >.gitattributes &&
18+
git add --renormalize "*.txt" &&
19+
cat >expect <<-\EOF &&
20+
i/lf w/crlf attr/text=auto CRLF.txt
21+
i/lf w/lf attr/text=auto LF.txt
22+
i/lf w/mixed attr/text=auto CRLF_mix_LF.txt
23+
EOF
24+
git ls-files --eol |
25+
sed -e "s/ / /g" -e "s/ */ /g" |
26+
sort >actual &&
27+
test_cmp expect actual
28+
'
29+
30+
test_done

0 commit comments

Comments
 (0)