1515#include  "gettext.h" 
1616#include  "revision.h" 
1717#include  "parse-options.h" 
18+ #include  "pathspec.h" 
1819#include  "string-list.h" 
1920#include  "dir.h" 
2021
21- static  int  read_directory_contents (const  char  * path , struct  string_list  * list )
22+ static  int  read_directory_contents (const  char  * path , struct  string_list  * list ,
23+ 				   const  struct  pathspec  * pathspec ,
24+ 				   int  skip )
2225{
26+ 	struct  strbuf  match  =  STRBUF_INIT ;
27+ 	int  len ;
2328	DIR  * dir ;
2429	struct  dirent  * e ;
2530
2631	if  (!(dir  =  opendir (path )))
2732		return  error ("Could not open directory %s" , path );
2833
29- 	while  ((e  =  readdir_skip_dot_and_dotdot (dir )))
34+ 	if  (pathspec ) {
35+ 		strbuf_addstr (& match , path );
36+ 		strbuf_complete (& match , '/' );
37+ 		strbuf_remove (& match , 0 , skip );
38+ 
39+ 		len  =  match .len ;
40+ 	}
41+ 
42+ 	while  ((e  =  readdir_skip_dot_and_dotdot (dir ))) {
43+ 		if  (pathspec ) {
44+ 			strbuf_setlen (& match , len );
45+ 			strbuf_addstr (& match , e -> d_name );
46+ 
47+ 			if  (!match_leading_pathspec (NULL , pathspec ,
48+ 						    match .buf , match .len ,
49+ 						    0 , NULL , e -> d_type  ==  DT_DIR  ? 1  : 0 ))
50+ 				continue ;
51+ 		}
52+ 
3053		string_list_insert (list , e -> d_name );
54+ 	}
3155
56+ 	strbuf_release (& match );
3257	closedir (dir );
3358	return  0 ;
3459}
@@ -131,7 +156,8 @@ static struct diff_filespec *noindex_filespec(const struct git_hash_algo *algop,
131156}
132157
133158static  int  queue_diff (struct  diff_options  * o , const  struct  git_hash_algo  * algop ,
134- 		      const  char  * name1 , const  char  * name2 , int  recursing )
159+ 		      const  char  * name1 , const  char  * name2 , int  recursing ,
160+ 		      const  struct  pathspec  * ps , int  skip1 , int  skip2 )
135161{
136162	int  mode1  =  0 , mode2  =  0 ;
137163	enum  special  special1  =  SPECIAL_NONE , special2  =  SPECIAL_NONE ;
@@ -171,9 +197,9 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
171197		int  i1 , i2 , ret  =  0 ;
172198		size_t  len1  =  0 , len2  =  0 ;
173199
174- 		if  (name1  &&  read_directory_contents (name1 , & p1 ))
200+ 		if  (name1  &&  read_directory_contents (name1 , & p1 ,  ps ,  skip1 ))
175201			return  -1 ;
176- 		if  (name2  &&  read_directory_contents (name2 , & p2 )) {
202+ 		if  (name2  &&  read_directory_contents (name2 , & p2 ,  ps ,  skip2 )) {
177203			string_list_clear (& p1 , 0 );
178204			return  -1 ;
179205		}
@@ -218,7 +244,7 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
218244				n2  =  buffer2 .buf ;
219245			}
220246
221- 			ret  =  queue_diff (o , algop , n1 , n2 , 1 );
247+ 			ret  =  queue_diff (o , algop , n1 , n2 , 1 ,  ps ,  skip1 ,  skip2 );
222248		}
223249		string_list_clear (& p1 , 0 );
224250		string_list_clear (& p2 , 0 );
@@ -258,8 +284,10 @@ static void append_basename(struct strbuf *path, const char *dir, const char *fi
258284 * DWIM "diff D F" into "diff D/F F" and "diff F D" into "diff F D/F" 
259285 * Note that we append the basename of F to D/, so "diff a/b/file D" 
260286 * becomes "diff a/b/file D/file", not "diff a/b/file D/a/b/file". 
287+  * 
288+  * Return 1 if both paths are directories, 0 otherwise. 
261289 */ 
262- static  void  fixup_paths (const  char  * * path , struct  strbuf  * replacement )
290+ static  int  fixup_paths (const  char  * * path , struct  strbuf  * replacement )
263291{
264292	struct  stat  st ;
265293	unsigned int   isdir0  =  0 , isdir1  =  0 ;
@@ -282,26 +310,31 @@ static void fixup_paths(const char **path, struct strbuf *replacement)
282310	if  ((isdir0  &&  ispipe1 ) ||  (ispipe0  &&  isdir1 ))
283311		die (_ ("cannot compare a named pipe to a directory" ));
284312
285- 	if  (isdir0  ==  isdir1 )
286- 		return ;
313+ 	/* if both paths are directories, we will enable pathspecs */ 
314+ 	if  (isdir0  &&  isdir1 )
315+ 		return  1 ;
316+ 
287317	if  (isdir0 ) {
288318		append_basename (replacement , path [0 ], path [1 ]);
289319		path [0 ] =  replacement -> buf ;
290- 	} else  {
320+ 	} else  if  ( isdir1 )  {
291321		append_basename (replacement , path [1 ], path [0 ]);
292322		path [1 ] =  replacement -> buf ;
293323	}
324+ 
325+ 	return  0 ;
294326}
295327
296328static  const  char  *  const  diff_no_index_usage [] =  {
297- 	N_ ("git diff --no-index [<options>] <path> <path>" ),
329+ 	N_ ("git diff --no-index [<options>] <path> <path> [<pathspec>...] " ),
298330	NULL 
299331};
300332
301333int  diff_no_index (struct  rev_info  * revs , const  struct  git_hash_algo  * algop ,
302334		  int  implicit_no_index , int  argc , const  char  * * argv )
303335{
304- 	int  i , no_index ;
336+ 	struct  pathspec  pathspec , * ps  =  NULL ;
337+ 	int  i , no_index , skip1  =  0 , skip2  =  0 ;
305338	int  ret  =  1 ;
306339	const  char  * paths [2 ];
307340	char  * to_free [ARRAY_SIZE (paths )] =  { 0  };
@@ -317,13 +350,12 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
317350	options  =  add_diff_options (no_index_options , & revs -> diffopt );
318351	argc  =  parse_options (argc , argv , revs -> prefix , options ,
319352			     diff_no_index_usage , 0 );
320- 	if  (argc  !=  2 ) {
353+ 	if  (argc  <  2 ) {
321354		if  (implicit_no_index )
322355			warning (_ ("Not a git repository. Use --no-index to " 
323356				  "compare two paths outside a working tree" ));
324357		usage_with_options (diff_no_index_usage , options );
325358	}
326- 	FREE_AND_NULL (options );
327359	for  (i  =  0 ; i  <  2 ; i ++ ) {
328360		const  char  * p  =  argv [i ];
329361		if  (!strcmp (p , "-" ))
@@ -337,7 +369,23 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
337369		paths [i ] =  p ;
338370	}
339371
340- 	fixup_paths (paths , & replacement );
372+ 	if  (fixup_paths (paths , & replacement )) {
373+ 		parse_pathspec (& pathspec , PATHSPEC_FROMTOP  | PATHSPEC_ATTR ,
374+ 			       PATHSPEC_PREFER_FULL  | PATHSPEC_NO_REPOSITORY ,
375+ 			       NULL , & argv [2 ]);
376+ 		if  (pathspec .nr )
377+ 			ps  =  & pathspec ;
378+ 
379+ 		skip1  =  strlen (paths [0 ]);
380+ 		skip1  +=  paths [0 ][skip1 ] ==  '/'  ? 0  : 1 ;
381+ 		skip2  =  strlen (paths [1 ]);
382+ 		skip2  +=  paths [1 ][skip2 ] ==  '/'  ? 0  : 1 ;
383+ 	} else  if  (argc  >  2 ) {
384+ 		warning (_ ("Limiting comparison with pathspecs is only " 
385+ 			  "supported if both paths are directories." ));
386+ 		usage_with_options (diff_no_index_usage , options );
387+ 	}
388+ 	FREE_AND_NULL (options );
341389
342390	revs -> diffopt .skip_stat_unmatch  =  1 ;
343391	if  (!revs -> diffopt .output_format )
@@ -354,7 +402,8 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
354402	setup_diff_pager (& revs -> diffopt );
355403	revs -> diffopt .flags .exit_with_status  =  1 ;
356404
357- 	if  (queue_diff (& revs -> diffopt , algop , paths [0 ], paths [1 ], 0 ))
405+ 	if  (queue_diff (& revs -> diffopt , algop , paths [0 ], paths [1 ], 0 , ps ,
406+ 		       skip1 , skip2 ))
358407		goto out ;
359408	diff_set_mnemonic_prefix (& revs -> diffopt , "1/" , "2/" );
360409	diffcore_std (& revs -> diffopt );
@@ -370,5 +419,7 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
370419	for  (i  =  0 ; i  <  ARRAY_SIZE (to_free ); i ++ )
371420		free (to_free [i ]);
372421	strbuf_release (& replacement );
422+ 	if  (ps )
423+ 		clear_pathspec (ps );
373424	return  ret ;
374425}
0 commit comments