1
- use bstr:: { BStr , BString } ;
2
-
3
- use crate :: blob:: pipeline:: DriverChoice ;
4
- use crate :: blob:: { pipeline, Pipeline , Platform , ResourceKind } ;
1
+ use crate :: blob:: { pipeline, BuiltinDriver , Pipeline , Platform , ResourceKind } ;
2
+ use bstr:: { BStr , BString , ByteSlice } ;
3
+ use gix_filter:: attributes;
5
4
6
5
/// A stored value representing a resource that participates in a merge.
7
6
#[ derive( Clone , Eq , PartialEq , Ord , PartialOrd , Debug ) ]
@@ -10,8 +9,8 @@ pub(super) struct Resource {
10
9
id : gix_hash:: ObjectId ,
11
10
/// The repository-relative path where the resource lives in the tree.
12
11
rela_path : BString ,
13
- /// The outcome of converting a resource into a diffable format using [Pipeline::convert_to_mergeable()].
14
- conversion : pipeline:: Outcome ,
12
+ /// The outcome of converting a resource into a mergable format using [Pipeline::convert_to_mergeable()].
13
+ data : Option < pipeline:: Data > ,
15
14
/// The kind of the resource we are looking at. Only possible values are `Blob` and `BlobExecutable`.
16
15
mode : gix_object:: tree:: EntryKind ,
17
16
/// A possibly empty buffer, depending on `conversion.data` which may indicate the data is considered binary
@@ -26,14 +25,51 @@ pub struct ResourceRef<'a> {
26
25
pub data : resource:: Data < ' a > ,
27
26
/// The location of the resource, relative to the working tree.
28
27
pub rela_path : & ' a BStr ,
29
- /// Which driver to use according to the resource's configuration.
30
- pub driver_choice : DriverChoice ,
31
28
/// The id of the content as it would be stored in `git`, or `null` if the content doesn't exist anymore at
32
29
/// `rela_path` or if it was never computed. This can happen with content read from the worktree, which
33
30
/// after its 'to-git' conversion never had its hash computed.
34
31
pub id : & ' a gix_hash:: oid ,
35
32
}
36
33
34
+ /// Options for use in a [`Platform`].
35
+ #[ derive( Default , Clone , PartialEq , Eq , Debug , Hash , Ord , PartialOrd ) ]
36
+ pub struct Options {
37
+ /// Define which driver to use by name if the `merge` attribute for a resource is unspecified.
38
+ ///
39
+ /// This is the value of the `merge.default` git configuration.
40
+ pub default_driver : Option < BString > ,
41
+ }
42
+
43
+ /// The selection of the driver to use by a resource obtained with [`Pipeline::convert_to_mergeable()`].
44
+ ///
45
+ /// If available, an index into the `drivers` field to access more diff-related information of the driver for items
46
+ /// at the given path, as previously determined by git-attributes.
47
+ ///
48
+ /// * `merge` is set
49
+ /// - Use the [`BuiltinDriver::Text`]
50
+ /// * `-merge` is unset
51
+ /// - Use the [`BuiltinDriver::Binary`]
52
+ /// * `!merge` is unspecified
53
+ /// - Use [`Options::default_driver`] or [`BuiltinDriver::Text`].
54
+ /// * `merge=name`
55
+ /// - Search for a user-configured or built-in driver called `name`.
56
+ /// - If not found, silently default to [`BuiltinDriver::Text`]
57
+ ///
58
+ /// Note that drivers are queried even if there is no object available.
59
+ #[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Debug , Hash ) ]
60
+ pub enum DriverChoice {
61
+ /// Use the given built-in driver to perform the merge.
62
+ BuiltIn ( BuiltinDriver ) ,
63
+ /// Use the user-provided driver program using the index into [the pipelines driver array](Pipeline::drivers().
64
+ Index ( usize ) ,
65
+ }
66
+
67
+ impl Default for DriverChoice {
68
+ fn default ( ) -> Self {
69
+ DriverChoice :: BuiltIn ( Default :: default ( ) )
70
+ }
71
+ }
72
+
37
73
///
38
74
pub mod resource {
39
75
use crate :: blob:: {
@@ -44,11 +80,10 @@ pub mod resource {
44
80
impl < ' a > ResourceRef < ' a > {
45
81
pub ( super ) fn new ( cache : & ' a Resource ) -> Self {
46
82
ResourceRef {
47
- data : cache. conversion . data . map_or ( Data :: Missing , |data| match data {
83
+ data : cache. data . map_or ( Data :: Missing , |data| match data {
48
84
pipeline:: Data :: Buffer => Data :: Buffer ( & cache. buffer ) ,
49
- pipeline:: Data :: Binary { size } => Data :: Binary { size } ,
85
+ pipeline:: Data :: TooLarge { size } => Data :: Binary { size } ,
50
86
} ) ,
51
- driver_choice : cache. conversion . driver ,
52
87
rela_path : cache. rela_path . as_ref ( ) ,
53
88
id : & cache. id ,
54
89
}
@@ -118,7 +153,7 @@ pub mod set_resource {
118
153
119
154
///
120
155
pub mod merge {
121
- use crate :: blob:: pipeline :: DriverChoice ;
156
+ use crate :: blob:: platform :: DriverChoice ;
122
157
use crate :: blob:: platform:: ResourceRef ;
123
158
use crate :: blob:: { builtin_driver, BuiltinDriver , Driver , Resolution } ;
124
159
use bstr:: BString ;
@@ -135,6 +170,9 @@ pub mod merge {
135
170
pub ancestor : ResourceRef < ' parent > ,
136
171
/// The other or their side of the merge operation.
137
172
pub other : ResourceRef < ' parent > ,
173
+ /// Which driver to use according to the resource's configuration,
174
+ /// using the path of `current` to read git-attributes.
175
+ pub driver_choice : DriverChoice ,
138
176
}
139
177
140
178
#[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
@@ -267,9 +305,9 @@ pub mod merge {
267
305
/// Return the configured driver program for use with [`Self::prepare_external_driver()`], or `Err`
268
306
/// with the built-in driver to use instead.
269
307
pub fn configured_driver ( & self ) -> Result < & ' parent Driver , BuiltinDriver > {
270
- match self . current . driver_choice {
308
+ match self . driver_choice {
271
309
DriverChoice :: BuiltIn ( builtin) => Err ( builtin) ,
272
- DriverChoice :: Index ( idx) => self . parent . filter . drivers . get ( idx) . ok_or ( BuiltinDriver :: default ( ) ) ,
310
+ DriverChoice :: Index ( idx) => self . parent . drivers . get ( idx) . ok_or ( BuiltinDriver :: default ( ) ) ,
273
311
}
274
312
}
275
313
}
@@ -299,14 +337,21 @@ pub mod merge {
299
337
300
338
///
301
339
pub mod prepare_merge {
340
+ use crate :: blob:: ResourceKind ;
341
+ use bstr:: BString ;
342
+
302
343
/// The error returned by [Platform::prepare_merge()](super::Platform::prepare_merge_state()).
303
344
#[ derive( Debug , thiserror:: Error ) ]
304
345
#[ allow( missing_docs) ]
305
346
pub enum Error {
306
347
#[ error( "The 'current', 'ancestor' or 'other' resource for the merge operation were not set" ) ]
307
348
UnsetResource ,
308
- #[ error( "Tried to merge 'current' and 'other' where at least one of them is removed" ) ]
309
- CurrentOrOtherRemoved ,
349
+ #[ error( "Failed to obtain attributes for {kind:?} resource at '{rela_path}'" ) ]
350
+ Attributes {
351
+ rela_path : BString ,
352
+ kind : ResourceKind ,
353
+ source : std:: io:: Error ,
354
+ } ,
310
355
}
311
356
}
312
357
@@ -315,18 +360,44 @@ impl Platform {
315
360
/// Create a new instance with a way to `filter` data from the object database and turn it into something that is merge-able.
316
361
/// `filter_mode` decides how to do that specifically.
317
362
/// Use `attr_stack` to access attributes pertaining worktree filters and merge settings.
318
- pub fn new ( filter : Pipeline , filter_mode : pipeline:: Mode , attr_stack : gix_worktree:: Stack ) -> Self {
363
+ /// `drivers` are the list of available merge drivers that individual paths can refer to by means of git attributes.
364
+ /// `options` further configure the operation.
365
+ pub fn new (
366
+ filter : Pipeline ,
367
+ filter_mode : pipeline:: Mode ,
368
+ attr_stack : gix_worktree:: Stack ,
369
+ mut drivers : Vec < super :: Driver > ,
370
+ options : Options ,
371
+ ) -> Self {
372
+ drivers. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
319
373
Platform {
374
+ drivers,
320
375
current : None ,
321
376
ancestor : None ,
322
377
other : None ,
323
378
filter,
324
379
filter_mode,
325
380
attr_stack,
381
+ attrs : {
382
+ let mut out = attributes:: search:: Outcome :: default ( ) ;
383
+ out. initialize_with_selection ( & Default :: default ( ) , Some ( "merge" ) ) ;
384
+ out
385
+ } ,
386
+ options,
326
387
}
327
388
}
328
389
}
329
390
391
+ /// Access
392
+ impl Platform {
393
+ /// Return all drivers that this instance was initialized with.
394
+ ///
395
+ /// They are sorted by [`name`](super::Driver::name) to support binary searches.
396
+ pub fn drivers ( & self ) -> & [ super :: Driver ] {
397
+ & self . drivers
398
+ }
399
+ }
400
+
330
401
/// Preparation
331
402
impl Platform {
332
403
/// Store enough information about a resource to eventually use it in a merge, where…
@@ -351,33 +422,62 @@ impl Platform {
351
422
self . set_resource_inner ( id, mode, rela_path, kind, objects)
352
423
}
353
424
354
- /// Returns the resource of the given kind if it was set.
355
- pub fn resource ( & self , kind : ResourceKind ) -> Option < ResourceRef < ' _ > > {
356
- let cache = match kind {
357
- ResourceKind :: CurrentOrOurs => self . current . as_ref ( ) ,
358
- ResourceKind :: CommonAncestorOrBase => self . ancestor . as_ref ( ) ,
359
- ResourceKind :: OtherOrTheirs => self . other . as_ref ( ) ,
360
- } ?;
361
- ResourceRef :: new ( cache) . into ( )
362
- }
363
-
364
425
/// Prepare all state needed for performing a merge, using all [previously set](Self::set_resource()) resources.
365
- pub fn prepare_merge_state ( & self ) -> Result < merge:: State < ' _ > , prepare_merge:: Error > {
426
+ /// Note that no additional validation is performed here to facilitate inspection.
427
+ pub fn prepare_merge_state (
428
+ & mut self ,
429
+ objects : & impl gix_object:: Find ,
430
+ ) -> Result < merge:: State < ' _ > , prepare_merge:: Error > {
366
431
let current = self . current . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
367
432
let ancestor = self . ancestor . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
368
433
let other = self . other . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
369
434
435
+ let entry = self
436
+ . attr_stack
437
+ . at_entry ( current. rela_path . as_bstr ( ) , None , objects)
438
+ . map_err ( |err| prepare_merge:: Error :: Attributes {
439
+ source : err,
440
+ kind : ResourceKind :: CurrentOrOurs ,
441
+ rela_path : current. rela_path . clone ( ) ,
442
+ } ) ?;
443
+ entry. matching_attributes ( & mut self . attrs ) ;
444
+ let attr = self . attrs . iter_selected ( ) . next ( ) . expect ( "pre-initialized with 'diff'" ) ;
445
+ let driver = match attr. assignment . state {
446
+ attributes:: StateRef :: Set => DriverChoice :: BuiltIn ( BuiltinDriver :: Text ) ,
447
+ attributes:: StateRef :: Unset => DriverChoice :: BuiltIn ( BuiltinDriver :: Binary ) ,
448
+ attributes:: StateRef :: Value ( _) | attributes:: StateRef :: Unspecified => {
449
+ let name = match attr. assignment . state {
450
+ attributes:: StateRef :: Value ( name) => Some ( name. as_bstr ( ) ) ,
451
+ attributes:: StateRef :: Unspecified => {
452
+ self . options . default_driver . as_ref ( ) . map ( |name| name. as_bstr ( ) )
453
+ }
454
+ _ => unreachable ! ( "only value and unspecified are possible here" ) ,
455
+ } ;
456
+ name. and_then ( |name| {
457
+ self . drivers
458
+ . binary_search_by ( |d| d. name . as_bstr ( ) . cmp ( name) )
459
+ . ok ( )
460
+ . map ( DriverChoice :: Index )
461
+ . or_else ( || {
462
+ name. to_str ( )
463
+ . ok ( )
464
+ . and_then ( BuiltinDriver :: by_name)
465
+ . map ( DriverChoice :: BuiltIn )
466
+ } )
467
+ } )
468
+ . unwrap_or_default ( )
469
+ }
470
+ } ;
471
+
370
472
let out = merge:: State {
371
473
parent : self ,
474
+ driver_choice : driver,
372
475
current : ResourceRef :: new ( current) ,
373
476
ancestor : ResourceRef :: new ( ancestor) ,
374
477
other : ResourceRef :: new ( other) ,
375
478
} ;
376
479
377
- match ( current. conversion . data , other. conversion . data ) {
378
- ( None , None ) => Err ( prepare_merge:: Error :: CurrentOrOtherRemoved ) ,
379
- ( _, _) => Ok ( out) ,
380
- }
480
+ Ok ( out)
381
481
}
382
482
}
383
483
@@ -430,15 +530,15 @@ impl Platform {
430
530
* storage = Some ( Resource {
431
531
id,
432
532
rela_path : rela_path. to_owned ( ) ,
433
- conversion : out,
533
+ data : out,
434
534
mode,
435
535
buffer : buf_storage,
436
536
} ) ;
437
537
}
438
538
Some ( storage) => {
439
539
storage. id = id;
440
540
storage. rela_path = rela_path. to_owned ( ) ;
441
- storage. conversion = out;
541
+ storage. data = out;
442
542
storage. mode = mode;
443
543
}
444
544
} ;
0 commit comments