@@ -33,6 +33,7 @@ static int mark_valid_only;
3333static int mark_skip_worktree_only ;
3434#define MARK_FLAG 1
3535#define UNMARK_FLAG 2
36+ static struct strbuf mtime_dir = STRBUF_INIT ;
3637
3738__attribute__((format (printf , 1 , 2 )))
3839static void report (const char * fmt , ...)
@@ -48,6 +49,166 @@ static void report(const char *fmt, ...)
4849 va_end (vp );
4950}
5051
52+ static void remove_test_directory (void )
53+ {
54+ if (mtime_dir .len )
55+ remove_dir_recursively (& mtime_dir , 0 );
56+ }
57+
58+ static const char * get_mtime_path (const char * path )
59+ {
60+ static struct strbuf sb = STRBUF_INIT ;
61+ strbuf_reset (& sb );
62+ strbuf_addf (& sb , "%s/%s" , mtime_dir .buf , path );
63+ return sb .buf ;
64+ }
65+
66+ static void xmkdir (const char * path )
67+ {
68+ path = get_mtime_path (path );
69+ if (mkdir (path , 0700 ))
70+ die_errno (_ ("failed to create directory %s" ), path );
71+ }
72+
73+ static int xstat_mtime_dir (struct stat * st )
74+ {
75+ if (stat (mtime_dir .buf , st ))
76+ die_errno (_ ("failed to stat %s" ), mtime_dir .buf );
77+ return 0 ;
78+ }
79+
80+ static int create_file (const char * path )
81+ {
82+ int fd ;
83+ path = get_mtime_path (path );
84+ fd = open (path , O_CREAT | O_RDWR , 0644 );
85+ if (fd < 0 )
86+ die_errno (_ ("failed to create file %s" ), path );
87+ return fd ;
88+ }
89+
90+ static void xunlink (const char * path )
91+ {
92+ path = get_mtime_path (path );
93+ if (unlink (path ))
94+ die_errno (_ ("failed to delete file %s" ), path );
95+ }
96+
97+ static void xrmdir (const char * path )
98+ {
99+ path = get_mtime_path (path );
100+ if (rmdir (path ))
101+ die_errno (_ ("failed to delete directory %s" ), path );
102+ }
103+
104+ static void avoid_racy (void )
105+ {
106+ /*
107+ * not use if we could usleep(10) if USE_NSEC is defined. The
108+ * field nsec could be there, but the OS could choose to
109+ * ignore it?
110+ */
111+ sleep (1 );
112+ }
113+
114+ static int test_if_untracked_cache_is_supported (void )
115+ {
116+ struct stat st ;
117+ struct stat_data base ;
118+ int fd , ret = 0 ;
119+
120+ strbuf_addstr (& mtime_dir , "mtime-test-XXXXXX" );
121+ if (!mkdtemp (mtime_dir .buf ))
122+ die_errno ("Could not make temporary directory" );
123+
124+ fprintf (stderr , _ ("Testing " ));
125+ atexit (remove_test_directory );
126+ xstat_mtime_dir (& st );
127+ fill_stat_data (& base , & st );
128+ fputc ('.' , stderr );
129+
130+ avoid_racy ();
131+ fd = create_file ("newfile" );
132+ xstat_mtime_dir (& st );
133+ if (!match_stat_data (& base , & st )) {
134+ close (fd );
135+ fputc ('\n' , stderr );
136+ fprintf_ln (stderr ,_ ("directory stat info does not "
137+ "change after adding a new file" ));
138+ goto done ;
139+ }
140+ fill_stat_data (& base , & st );
141+ fputc ('.' , stderr );
142+
143+ avoid_racy ();
144+ xmkdir ("new-dir" );
145+ xstat_mtime_dir (& st );
146+ if (!match_stat_data (& base , & st )) {
147+ close (fd );
148+ fputc ('\n' , stderr );
149+ fprintf_ln (stderr , _ ("directory stat info does not change "
150+ "after adding a new directory" ));
151+ goto done ;
152+ }
153+ fill_stat_data (& base , & st );
154+ fputc ('.' , stderr );
155+
156+ avoid_racy ();
157+ write_or_die (fd , "data" , 4 );
158+ close (fd );
159+ xstat_mtime_dir (& st );
160+ if (match_stat_data (& base , & st )) {
161+ fputc ('\n' , stderr );
162+ fprintf_ln (stderr , _ ("directory stat info changes "
163+ "after updating a file" ));
164+ goto done ;
165+ }
166+ fputc ('.' , stderr );
167+
168+ avoid_racy ();
169+ close (create_file ("new-dir/new" ));
170+ xstat_mtime_dir (& st );
171+ if (match_stat_data (& base , & st )) {
172+ fputc ('\n' , stderr );
173+ fprintf_ln (stderr , _ ("directory stat info changes after "
174+ "adding a file inside subdirectory" ));
175+ goto done ;
176+ }
177+ fputc ('.' , stderr );
178+
179+ avoid_racy ();
180+ xunlink ("newfile" );
181+ xstat_mtime_dir (& st );
182+ if (!match_stat_data (& base , & st )) {
183+ fputc ('\n' , stderr );
184+ fprintf_ln (stderr , _ ("directory stat info does not "
185+ "change after deleting a file" ));
186+ goto done ;
187+ }
188+ fill_stat_data (& base , & st );
189+ fputc ('.' , stderr );
190+
191+ avoid_racy ();
192+ xunlink ("new-dir/new" );
193+ xrmdir ("new-dir" );
194+ xstat_mtime_dir (& st );
195+ if (!match_stat_data (& base , & st )) {
196+ fputc ('\n' , stderr );
197+ fprintf_ln (stderr , _ ("directory stat info does not "
198+ "change after deleting a directory" ));
199+ goto done ;
200+ }
201+
202+ if (rmdir (mtime_dir .buf ))
203+ die_errno (_ ("failed to delete directory %s" ), mtime_dir .buf );
204+ fprintf_ln (stderr , _ (" OK" ));
205+ ret = 1 ;
206+
207+ done :
208+ strbuf_release (& mtime_dir );
209+ return ret ;
210+ }
211+
51212static int mark_ce_flags (const char * path , int flag , int mark )
52213{
53214 int namelen = strlen (path );
@@ -741,6 +902,7 @@ static int reupdate_callback(struct parse_opt_ctx_t *ctx,
741902int cmd_update_index (int argc , const char * * argv , const char * prefix )
742903{
743904 int newfd , entries , has_errors = 0 , line_termination = '\n' ;
905+ int untracked_cache = -1 ;
744906 int read_from_stdin = 0 ;
745907 int prefix_length = prefix ? strlen (prefix ) : 0 ;
746908 int preferred_index_format = 0 ;
@@ -832,6 +994,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
832994 N_ ("write index in this format" )),
833995 OPT_BOOL (0 , "split-index" , & split_index ,
834996 N_ ("enable or disable split index" )),
997+ OPT_BOOL (0 , "untracked-cache" , & untracked_cache ,
998+ N_ ("enable/disable untracked cache" )),
999+ OPT_SET_INT (0 , "force-untracked-cache" , & untracked_cache ,
1000+ N_ ("enable untracked cache without testing the filesystem" ), 2 ),
8351001 OPT_END ()
8361002 };
8371003
@@ -938,6 +1104,28 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
9381104 the_index .split_index = NULL ;
9391105 the_index .cache_changed |= SOMETHING_CHANGED ;
9401106 }
1107+ if (untracked_cache > 0 ) {
1108+ struct untracked_cache * uc ;
1109+
1110+ if (untracked_cache < 2 ) {
1111+ setup_work_tree ();
1112+ if (!test_if_untracked_cache_is_supported ())
1113+ return 1 ;
1114+ }
1115+ if (!the_index .untracked ) {
1116+ uc = xcalloc (1 , sizeof (* uc ));
1117+ strbuf_init (& uc -> ident , 100 );
1118+ uc -> exclude_per_dir = ".gitignore" ;
1119+ /* should be the same flags used by git-status */
1120+ uc -> dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES ;
1121+ the_index .untracked = uc ;
1122+ }
1123+ add_untracked_ident (the_index .untracked );
1124+ the_index .cache_changed |= UNTRACKED_CHANGED ;
1125+ } else if (!untracked_cache && the_index .untracked ) {
1126+ the_index .untracked = NULL ;
1127+ the_index .cache_changed |= UNTRACKED_CHANGED ;
1128+ }
9411129
9421130 if (active_cache_changed ) {
9431131 if (newfd < 0 ) {
0 commit comments