@@ -97,8 +97,27 @@ static int queue_diff(struct diff_options *o,
97
97
if (get_mode (name1 , & mode1 ) || get_mode (name2 , & mode2 ))
98
98
return -1 ;
99
99
100
- if (mode1 && mode2 && S_ISDIR (mode1 ) != S_ISDIR (mode2 ))
101
- return error ("file/directory conflict: %s, %s" , name1 , name2 );
100
+ if (mode1 && mode2 && S_ISDIR (mode1 ) != S_ISDIR (mode2 )) {
101
+ struct diff_filespec * d1 , * d2 ;
102
+
103
+ if (S_ISDIR (mode1 )) {
104
+ /* 2 is file that is created */
105
+ d1 = noindex_filespec (NULL , 0 );
106
+ d2 = noindex_filespec (name2 , mode2 );
107
+ name2 = NULL ;
108
+ mode2 = 0 ;
109
+ } else {
110
+ /* 1 is file that is deleted */
111
+ d1 = noindex_filespec (name1 , mode1 );
112
+ d2 = noindex_filespec (NULL , 0 );
113
+ name1 = NULL ;
114
+ mode1 = 0 ;
115
+ }
116
+ /* emit that file */
117
+ diff_queue (& diff_queued_diff , d1 , d2 );
118
+
119
+ /* and then let the entire directory be created or deleted */
120
+ }
102
121
103
122
if (S_ISDIR (mode1 ) || S_ISDIR (mode2 )) {
104
123
struct strbuf buffer1 = STRBUF_INIT ;
@@ -182,12 +201,50 @@ static int queue_diff(struct diff_options *o,
182
201
}
183
202
}
184
203
204
+ /* append basename of F to D */
205
+ static void append_basename (struct strbuf * path , const char * dir , const char * file )
206
+ {
207
+ const char * tail = strrchr (file , '/' );
208
+
209
+ strbuf_addstr (path , dir );
210
+ while (path -> len && path -> buf [path -> len - 1 ] == '/' )
211
+ path -> len -- ;
212
+ strbuf_addch (path , '/' );
213
+ strbuf_addstr (path , tail ? tail + 1 : file );
214
+ }
215
+
216
+ /*
217
+ * DWIM "diff D F" into "diff D/F F" and "diff F D" into "diff F D/F"
218
+ * Note that we append the basename of F to D/, so "diff a/b/file D"
219
+ * becomes "diff a/b/file D/file", not "diff a/b/file D/a/b/file".
220
+ */
221
+ static void fixup_paths (const char * * path , struct strbuf * replacement )
222
+ {
223
+ unsigned int isdir0 , isdir1 ;
224
+
225
+ if (path [0 ] == file_from_standard_input ||
226
+ path [1 ] == file_from_standard_input )
227
+ return ;
228
+ isdir0 = is_directory (path [0 ]);
229
+ isdir1 = is_directory (path [1 ]);
230
+ if (isdir0 == isdir1 )
231
+ return ;
232
+ if (isdir0 ) {
233
+ append_basename (replacement , path [0 ], path [1 ]);
234
+ path [0 ] = replacement -> buf ;
235
+ } else {
236
+ append_basename (replacement , path [1 ], path [0 ]);
237
+ path [1 ] = replacement -> buf ;
238
+ }
239
+ }
240
+
185
241
void diff_no_index (struct rev_info * revs ,
186
242
int argc , const char * * argv ,
187
243
const char * prefix )
188
244
{
189
245
int i , prefixlen ;
190
246
const char * paths [2 ];
247
+ struct strbuf replacement = STRBUF_INIT ;
191
248
192
249
diff_setup (& revs -> diffopt );
193
250
for (i = 1 ; i < argc - 2 ; ) {
@@ -217,6 +274,9 @@ void diff_no_index(struct rev_info *revs,
217
274
p = xstrdup (prefix_filename (prefix , prefixlen , p ));
218
275
paths [i ] = p ;
219
276
}
277
+
278
+ fixup_paths (paths , & replacement );
279
+
220
280
revs -> diffopt .skip_stat_unmatch = 1 ;
221
281
if (!revs -> diffopt .output_format )
222
282
revs -> diffopt .output_format = DIFF_FORMAT_PATCH ;
@@ -235,6 +295,8 @@ void diff_no_index(struct rev_info *revs,
235
295
diffcore_std (& revs -> diffopt );
236
296
diff_flush (& revs -> diffopt );
237
297
298
+ strbuf_release (& replacement );
299
+
238
300
/*
239
301
* The return code for --no-index imitates diff(1):
240
302
* 0 = no changes, 1 = changes, else error
0 commit comments