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