Skip to content

Commit b0c2d61

Browse files
pcloudsgitster
authored andcommitted
Introduce "precious" file concept
A new attribute "precious" is added to indicate that certain files have valuable content and should not be easily discarded even if they are ignored or untracked. So far there is one part of Git that is made aware of precious files: "git clean" will leave precious files alone if --keep-precious is specified. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 041f5ea commit b0c2d61

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
-----------
@@ -71,6 +71,10 @@ OPTIONS
7171
Remove only files ignored by Git. This may be useful to rebuild
7272
everything from scratch, but keep manually created files.
7373

74+
--keep-precious::
75+
Do not remove untracked or ignored files if they have
76+
`precious` attribute.
77+
7478
Interactive mode
7579
----------------
7680
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
@@ -1192,6 +1192,17 @@ If this attribute is not set or has an invalid value, the value of the
11921192
(See linkgit:git-config[1]).
11931193

11941194

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

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)