@@ -100,6 +100,106 @@ static int sparse_checkout_list(int argc, const char **argv)
100
100
return 0 ;
101
101
}
102
102
103
+ static void clean_tracked_sparse_directories (struct repository * r )
104
+ {
105
+ int i , value , was_full = 0 ;
106
+ struct strbuf path = STRBUF_INIT ;
107
+ size_t pathlen ;
108
+ struct string_list_item * item ;
109
+ struct string_list sparse_dirs = STRING_LIST_INIT_DUP ;
110
+
111
+ /*
112
+ * If we are not using cone mode patterns, then we cannot
113
+ * delete directories outside of the sparse cone.
114
+ */
115
+ if (!r || !r -> index || !r -> worktree )
116
+ return ;
117
+ init_sparse_checkout_patterns (r -> index );
118
+ if (!r -> index -> sparse_checkout_patterns ||
119
+ !r -> index -> sparse_checkout_patterns -> use_cone_patterns )
120
+ return ;
121
+
122
+ /*
123
+ * Users can disable this behavior.
124
+ */
125
+ if (!repo_config_get_bool (r , "index.deletesparsedirectories" , & value ) &&
126
+ !value )
127
+ return ;
128
+
129
+ /*
130
+ * Use the sparse index as a data structure to assist finding
131
+ * directories that are safe to delete. This conversion to a
132
+ * sparse index will not delete directories that contain
133
+ * conflicted entries or submodules.
134
+ */
135
+ if (!r -> index -> sparse_index ) {
136
+ /*
137
+ * If something, such as a merge conflict or other concern,
138
+ * prevents us from converting to a sparse index, then do
139
+ * not try deleting files.
140
+ */
141
+ if (convert_to_sparse (r -> index , SPARSE_INDEX_MEMORY_ONLY ))
142
+ return ;
143
+ was_full = 1 ;
144
+ }
145
+
146
+ strbuf_addstr (& path , r -> worktree );
147
+ strbuf_complete (& path , '/' );
148
+ pathlen = path .len ;
149
+
150
+ /*
151
+ * Collect directories that have gone out of scope but also
152
+ * exist on disk, so there is some work to be done. We need to
153
+ * store the entries in a list before exploring, since that might
154
+ * expand the sparse-index again.
155
+ */
156
+ for (i = 0 ; i < r -> index -> cache_nr ; i ++ ) {
157
+ struct cache_entry * ce = r -> index -> cache [i ];
158
+
159
+ if (S_ISSPARSEDIR (ce -> ce_mode ) &&
160
+ repo_file_exists (r , ce -> name ))
161
+ string_list_append (& sparse_dirs , ce -> name );
162
+ }
163
+
164
+ for_each_string_list_item (item , & sparse_dirs ) {
165
+ struct dir_struct dir = DIR_INIT ;
166
+ struct pathspec p = { 0 };
167
+ struct strvec s = STRVEC_INIT ;
168
+
169
+ strbuf_setlen (& path , pathlen );
170
+ strbuf_addstr (& path , item -> string );
171
+
172
+ dir .flags |= DIR_SHOW_IGNORED_TOO ;
173
+
174
+ setup_standard_excludes (& dir );
175
+ strvec_push (& s , path .buf );
176
+
177
+ parse_pathspec (& p , PATHSPEC_GLOB , 0 , NULL , s .v );
178
+ fill_directory (& dir , r -> index , & p );
179
+
180
+ if (dir .nr ) {
181
+ warning (_ ("directory '%s' contains untracked files,"
182
+ " but is not in the sparse-checkout cone" ),
183
+ item -> string );
184
+ } else if (remove_dir_recursively (& path , 0 )) {
185
+ /*
186
+ * Removal is "best effort". If something blocks
187
+ * the deletion, then continue with a warning.
188
+ */
189
+ warning (_ ("failed to remove directory '%s'" ),
190
+ item -> string );
191
+ }
192
+
193
+ dir_clear (& dir );
194
+ }
195
+
196
+ string_list_clear (& sparse_dirs , 0 );
197
+ strbuf_release (& path );
198
+
199
+ if (was_full )
200
+ ensure_full_index (r -> index );
201
+ }
202
+
103
203
static int update_working_directory (struct pattern_list * pl )
104
204
{
105
205
enum update_sparsity_result result ;
@@ -141,6 +241,8 @@ static int update_working_directory(struct pattern_list *pl)
141
241
else
142
242
rollback_lock_file (& lock_file );
143
243
244
+ clean_tracked_sparse_directories (r );
245
+
144
246
r -> index -> sparse_checkout_patterns = NULL ;
145
247
return result ;
146
248
}
0 commit comments