Skip to content

Commit 07e61e6

Browse files
committed
Merge branch 'nd/precious' into jch
"git clean" learned to pay attention to the 'precious' attributes and keep untracked paths with the attribute instead of removing when the "--keep-precious" is given. Retracted. cf. <CACsJy8AEZ-Lz6zgEsuNukvphB9TTa9FAC1gK05fhnie2xtfc9w@mail.gmail.com> I am not sure what aspect of this longer-term "precious" vision, which gets taught to various commands and use cases individually and incrementally, Ævar finds problematic, which I understand is the reason of redtraction. * nd/precious: Introduce "precious" file concept
2 parents 8d56fa0 + b0c2d61 commit 07e61e6

File tree

6 files changed

+96
-5
lines changed

6 files changed

+96
-5
lines changed

Documentation/git-clean.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
88
SYNOPSIS
99
--------
1010
[verse]
11-
'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
11+
'git clean' [<options>] [-d] [-f] [-i] [-n] [-x | -X] [--] <path>...
1212

1313
DESCRIPTION
1414
-----------
@@ -70,6 +70,10 @@ OPTIONS
7070
Remove only files ignored by Git. This may be useful to rebuild
7171
everything from scratch, but keep manually created files.
7272

73+
--keep-precious::
74+
Do not remove untracked or ignored files if they have
75+
`precious` attribute.
76+
7377
Interactive mode
7478
----------------
7579
When the command enters the interactive mode, it shows the

Documentation/gitattributes.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,17 @@ If this attribute is not set or has an invalid value, the value of the
11931193
(See linkgit:git-config[1]).
11941194

11951195

1196+
Precious files
1197+
~~~~~~~~~~~~~~
1198+
1199+
`precious`
1200+
^^^^^^^^^^
1201+
1202+
This attribute is set on files to indicate that their content is
1203+
valuable. Some commands will behave slightly different on precious
1204+
files. linkgit:git-clean[1] may leave precious files alone.
1205+
1206+
11961207
USING MACRO ATTRIBUTES
11971208
----------------------
11981209

attr.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,3 +1157,15 @@ void attr_start(void)
11571157
pthread_mutex_init(&g_attr_hashmap.mutex, NULL);
11581158
pthread_mutex_init(&check_vector.mutex, NULL);
11591159
}
1160+
1161+
int is_precious_file(struct index_state *istate, const char *path)
1162+
{
1163+
static struct attr_check *check;
1164+
if (!check)
1165+
check = attr_check_initl("precious", NULL);
1166+
if (!check)
1167+
return 0;
1168+
1169+
git_check_attr(istate, path, check);
1170+
return ATTR_TRUE(check->items[0].value);
1171+
}

attr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ void git_attr_set_direction(enum git_attr_direction new_direction);
8282

8383
void attr_start(void);
8484

85+
int is_precious_file(struct index_state *istate, const char *path);
86+
8587
#endif /* ATTR_H */

builtin/clean.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,23 @@
1818
#include "color.h"
1919
#include "pathspec.h"
2020
#include "help.h"
21+
#include "attr.h"
2122

2223
static int force = -1; /* unset */
2324
static int interactive;
25+
static int keep_precious;
2426
static struct string_list del_list = STRING_LIST_INIT_DUP;
2527
static unsigned int colopts;
2628

2729
static const char *const builtin_clean_usage[] = {
28-
N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
30+
N_("git clean [<options>] [-d] [-f] [-i] [-n] [-x | -X] [--] <paths>..."),
2931
NULL
3032
};
3133

3234
static const char *msg_remove = N_("Removing %s\n");
3335
static const char *msg_would_remove = N_("Would remove %s\n");
36+
static const char *msg_skip_precious = N_("Skipping precious file %s\n");
37+
static const char *msg_would_skip_precious = N_("Would skip precious file %s\n");
3438
static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
3539
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
3640
static const char *msg_warn_remove_failed = N_("failed to remove %s");
@@ -146,6 +150,11 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
146150
return 0;
147151
}
148152

153+
static int skip_precious_file(struct index_state *istate, const char *path)
154+
{
155+
return keep_precious && is_precious_file(istate, path);
156+
}
157+
149158
static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
150159
int dry_run, int quiet, int *dir_gone)
151160
{
@@ -154,6 +163,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
154163
struct dirent *e;
155164
int res = 0, ret = 0, gone = 1, original_len = path->len, len;
156165
struct string_list dels = STRING_LIST_INIT_DUP;
166+
const char *rel_path;
157167

158168
*dir_gone = 1;
159169

@@ -193,9 +203,16 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
193203

194204
strbuf_setlen(path, len);
195205
strbuf_addstr(path, e->d_name);
196-
if (lstat(path->buf, &st))
206+
if (lstat(path->buf, &st)) {
197207
; /* fall thru */
198-
else if (S_ISDIR(st.st_mode)) {
208+
} else if ((!prefix && skip_precious_file(&the_index, path->buf)) ||
209+
(prefix && skip_prefix(path->buf, prefix, &rel_path) &&
210+
skip_precious_file(&the_index, rel_path))) {
211+
quote_path_relative(path->buf, prefix, &quoted);
212+
printf(dry_run ? _(msg_would_skip_precious) : _(msg_skip_precious), quoted.buf);
213+
*dir_gone = 0;
214+
continue;
215+
} else if (S_ISDIR(st.st_mode)) {
199216
if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
200217
ret = 1;
201218
if (gone) {
@@ -915,6 +932,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
915932
OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
916933
OPT_BOOL('X', NULL, &ignored_only,
917934
N_("remove only ignored files")),
935+
OPT_BOOL(0, "keep-precious", &keep_precious,
936+
N_("do not remove files with 'precious' attribute")),
918937
OPT_END()
919938
};
920939

@@ -1019,7 +1038,10 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
10191038
if (lstat(abs_path.buf, &st))
10201039
continue;
10211040

1022-
if (S_ISDIR(st.st_mode)) {
1041+
if (skip_precious_file(&the_index, item->string)) {
1042+
qname = quote_path_relative(item->string, NULL, &buf);
1043+
printf(dry_run ? _(msg_would_skip_precious) : _(msg_skip_precious), qname);
1044+
} else if (S_ISDIR(st.st_mode)) {
10231045
if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
10241046
errors++;
10251047
if (gone && !quiet) {

t/t7300-clean.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,4 +669,44 @@ test_expect_success 'git clean -d skips untracked dirs containing ignored files'
669669
test_path_is_missing foo/b/bb
670670
'
671671

672+
test_expect_success 'git clean -xd --keep-precious leaves precious files alone' '
673+
git init precious &&
674+
(
675+
cd precious &&
676+
test_commit one &&
677+
cat >.gitignore <<-\EOF &&
678+
*.o
679+
*.mak
680+
EOF
681+
cat >.gitattributes <<-\EOF &&
682+
*.mak precious
683+
.gitattributes precious
684+
*.precious precious
685+
EOF
686+
mkdir sub &&
687+
touch one.o sub/two.o one.mak sub/two.mak &&
688+
touch one.untracked two.precious sub/also.precious &&
689+
git clean -fdx --keep-precious &&
690+
test_path_is_missing one.o &&
691+
test_path_is_missing sub/two.o &&
692+
test_path_is_missing one.untracked &&
693+
test_path_is_file .gitattributes &&
694+
test_path_is_file one.mak &&
695+
test_path_is_file sub/two.mak &&
696+
test_path_is_file two.precious &&
697+
test_path_is_file sub/also.precious
698+
)
699+
'
700+
701+
test_expect_success 'git clean -xd still deletes them all' '
702+
test_path_is_file precious/one.mak &&
703+
test_path_is_file precious/sub/two.mak &&
704+
test_path_is_file precious/two.precious &&
705+
test_path_is_file precious/sub/also.precious &&
706+
git -C precious clean -fdx &&
707+
test_path_is_missing precious/one.mak &&
708+
test_path_is_missing precious/sub/two.mak &&
709+
test_path_is_missing precious/two.precious &&
710+
test_path_is_missing precious/sub/also.precious
711+
'
672712
test_done

0 commit comments

Comments
 (0)