15
15
#include "gettext.h"
16
16
#include "revision.h"
17
17
#include "parse-options.h"
18
+ #include "pathspec.h"
18
19
#include "string-list.h"
19
20
#include "dir.h"
20
21
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 )
22
25
{
26
+ struct strbuf match = STRBUF_INIT ;
27
+ int len ;
23
28
DIR * dir ;
24
29
struct dirent * e ;
25
30
26
31
if (!(dir = opendir (path )))
27
32
return error ("Could not open directory %s" , path );
28
33
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
+
30
53
string_list_insert (list , e -> d_name );
54
+ }
31
55
56
+ strbuf_release (& match );
32
57
closedir (dir );
33
58
return 0 ;
34
59
}
@@ -131,7 +156,8 @@ static struct diff_filespec *noindex_filespec(const struct git_hash_algo *algop,
131
156
}
132
157
133
158
static 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 )
135
161
{
136
162
int mode1 = 0 , mode2 = 0 ;
137
163
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,
171
197
int i1 , i2 , ret = 0 ;
172
198
size_t len1 = 0 , len2 = 0 ;
173
199
174
- if (name1 && read_directory_contents (name1 , & p1 ))
200
+ if (name1 && read_directory_contents (name1 , & p1 , ps , skip1 ))
175
201
return -1 ;
176
- if (name2 && read_directory_contents (name2 , & p2 )) {
202
+ if (name2 && read_directory_contents (name2 , & p2 , ps , skip2 )) {
177
203
string_list_clear (& p1 , 0 );
178
204
return -1 ;
179
205
}
@@ -218,7 +244,7 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
218
244
n2 = buffer2 .buf ;
219
245
}
220
246
221
- ret = queue_diff (o , algop , n1 , n2 , 1 );
247
+ ret = queue_diff (o , algop , n1 , n2 , 1 , ps , skip1 , skip2 );
222
248
}
223
249
string_list_clear (& p1 , 0 );
224
250
string_list_clear (& p2 , 0 );
@@ -258,8 +284,10 @@ static void append_basename(struct strbuf *path, const char *dir, const char *fi
258
284
* DWIM "diff D F" into "diff D/F F" and "diff F D" into "diff F D/F"
259
285
* Note that we append the basename of F to D/, so "diff a/b/file D"
260
286
* 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.
261
289
*/
262
- static void fixup_paths (const char * * path , struct strbuf * replacement )
290
+ static int fixup_paths (const char * * path , struct strbuf * replacement )
263
291
{
264
292
struct stat st ;
265
293
unsigned int isdir0 = 0 , isdir1 = 0 ;
@@ -282,26 +310,31 @@ static void fixup_paths(const char **path, struct strbuf *replacement)
282
310
if ((isdir0 && ispipe1 ) || (ispipe0 && isdir1 ))
283
311
die (_ ("cannot compare a named pipe to a directory" ));
284
312
285
- if (isdir0 == isdir1 )
286
- return ;
313
+ /* if both paths are directories, we will enable pathspecs */
314
+ if (isdir0 && isdir1 )
315
+ return 1 ;
316
+
287
317
if (isdir0 ) {
288
318
append_basename (replacement , path [0 ], path [1 ]);
289
319
path [0 ] = replacement -> buf ;
290
- } else {
320
+ } else if ( isdir1 ) {
291
321
append_basename (replacement , path [1 ], path [0 ]);
292
322
path [1 ] = replacement -> buf ;
293
323
}
324
+
325
+ return 0 ;
294
326
}
295
327
296
328
static 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>...] " ),
298
330
NULL
299
331
};
300
332
301
333
int diff_no_index (struct rev_info * revs , const struct git_hash_algo * algop ,
302
334
int implicit_no_index , int argc , const char * * argv )
303
335
{
304
- int i , no_index ;
336
+ struct pathspec pathspec , * ps = NULL ;
337
+ int i , no_index , skip1 = 0 , skip2 = 0 ;
305
338
int ret = 1 ;
306
339
const char * paths [2 ];
307
340
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,
317
350
options = add_diff_options (no_index_options , & revs -> diffopt );
318
351
argc = parse_options (argc , argv , revs -> prefix , options ,
319
352
diff_no_index_usage , 0 );
320
- if (argc != 2 ) {
353
+ if (argc < 2 ) {
321
354
if (implicit_no_index )
322
355
warning (_ ("Not a git repository. Use --no-index to "
323
356
"compare two paths outside a working tree" ));
324
357
usage_with_options (diff_no_index_usage , options );
325
358
}
326
- FREE_AND_NULL (options );
327
359
for (i = 0 ; i < 2 ; i ++ ) {
328
360
const char * p = argv [i ];
329
361
if (!strcmp (p , "-" ))
@@ -337,7 +369,23 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
337
369
paths [i ] = p ;
338
370
}
339
371
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 );
341
389
342
390
revs -> diffopt .skip_stat_unmatch = 1 ;
343
391
if (!revs -> diffopt .output_format )
@@ -354,7 +402,8 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
354
402
setup_diff_pager (& revs -> diffopt );
355
403
revs -> diffopt .flags .exit_with_status = 1 ;
356
404
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 ))
358
407
goto out ;
359
408
diff_set_mnemonic_prefix (& revs -> diffopt , "1/" , "2/" );
360
409
diffcore_std (& revs -> diffopt );
@@ -370,5 +419,7 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
370
419
for (i = 0 ; i < ARRAY_SIZE (to_free ); i ++ )
371
420
free (to_free [i ]);
372
421
strbuf_release (& replacement );
422
+ if (ps )
423
+ clear_pathspec (ps );
373
424
return ret ;
374
425
}
0 commit comments