Skip to content

Commit af6e0fe

Browse files
committed
Merge branch 'tb/add-renormalize'
"git add --renormalize ." is a new and safer way to record the fact that you are correcting the end-of-line convention and other "convert_to_git()" glitches in the in-repository data. * tb/add-renormalize: add: introduce "--renormalize"
2 parents 93bfe62 + 9472935 commit af6e0fe

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
@@ -711,6 +711,7 @@ extern int ie_modified(struct index_state *, const struct cache_entry *, struct
711711

712712
#define HASH_WRITE_OBJECT 1
713713
#define HASH_FORMAT_CHECK 2
714+
#define HASH_RENORMALIZE 4
714715
extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
715716
extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
716717

read-cache.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -641,13 +641,17 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
641641
{
642642
int size, namelen, was_same;
643643
mode_t st_mode = st->st_mode;
644-
struct cache_entry *ce, *alias;
644+
struct cache_entry *ce, *alias = NULL;
645645
unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
646646
int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
647647
int pretend = flags & ADD_CACHE_PRETEND;
648648
int intent_only = flags & ADD_CACHE_INTENT;
649649
int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
650650
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
651+
int newflags = HASH_WRITE_OBJECT;
652+
653+
if (flags & HASH_RENORMALIZE)
654+
newflags |= HASH_RENORMALIZE;
651655

652656
if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
653657
return error("%s: can only add regular files, symbolic links or git-directories", path);
@@ -688,19 +692,23 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
688692
if (ignore_case) {
689693
adjust_dirname_case(istate, ce->name);
690694
}
695+
if (!(flags & HASH_RENORMALIZE)) {
696+
alias = index_file_exists(istate, ce->name,
697+
ce_namelen(ce), ignore_case);
698+
if (alias &&
699+
!ce_stage(alias) &&
700+
!ie_match_stat(istate, alias, st, ce_option)) {
701+
/* Nothing changed, really */
702+
if (!S_ISGITLINK(alias->ce_mode))
703+
ce_mark_uptodate(alias);
704+
alias->ce_flags |= CE_ADDED;
691705

692-
alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
693-
if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
694-
/* Nothing changed, really */
695-
if (!S_ISGITLINK(alias->ce_mode))
696-
ce_mark_uptodate(alias);
697-
alias->ce_flags |= CE_ADDED;
698-
699-
free(ce);
700-
return 0;
706+
free(ce);
707+
return 0;
708+
}
701709
}
702710
if (!intent_only) {
703-
if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
711+
if (index_path(&ce->oid, path, st, newflags)) {
704712
free(ce);
705713
return error("unable to index file %s", path);
706714
}

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)) {
@@ -1679,7 +1691,7 @@ static int index_mem(struct object_id *oid, void *buf, size_t size,
16791691
if ((type == OBJ_BLOB) && path) {
16801692
struct strbuf nbuf = STRBUF_INIT;
16811693
if (convert_to_git(&the_index, path, buf, size, &nbuf,
1682-
write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
1694+
get_safe_crlf(flags))) {
16831695
buf = strbuf_detach(&nbuf, &size);
16841696
re_allocated = 1;
16851697
}
@@ -1713,7 +1725,7 @@ static int index_stream_convert_blob(struct object_id *oid, int fd,
17131725
assert(would_convert_to_git_filter_fd(path));
17141726

17151727
convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
1716-
write_object ? safe_crlf : SAFE_CRLF_FALSE);
1728+
get_safe_crlf(flags));
17171729

17181730
if (write_object)
17191731
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)