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;
54
65/// A stored value representing a resource that participates in a merge.
76#[ derive( Clone , Eq , PartialEq , Ord , PartialOrd , Debug ) ]
@@ -10,8 +9,8 @@ pub(super) struct Resource {
109 id : gix_hash:: ObjectId ,
1110 /// The repository-relative path where the resource lives in the tree.
1211 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 > ,
1514 /// The kind of the resource we are looking at. Only possible values are `Blob` and `BlobExecutable`.
1615 mode : gix_object:: tree:: EntryKind ,
1716 /// A possibly empty buffer, depending on `conversion.data` which may indicate the data is considered binary
@@ -26,14 +25,51 @@ pub struct ResourceRef<'a> {
2625 pub data : resource:: Data < ' a > ,
2726 /// The location of the resource, relative to the working tree.
2827 pub rela_path : & ' a BStr ,
29- /// Which driver to use according to the resource's configuration.
30- pub driver_choice : DriverChoice ,
3128 /// The id of the content as it would be stored in `git`, or `null` if the content doesn't exist anymore at
3229 /// `rela_path` or if it was never computed. This can happen with content read from the worktree, which
3330 /// after its 'to-git' conversion never had its hash computed.
3431 pub id : & ' a gix_hash:: oid ,
3532}
3633
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+
3773///
3874pub mod resource {
3975 use crate :: blob:: {
@@ -44,11 +80,10 @@ pub mod resource {
4480 impl < ' a > ResourceRef < ' a > {
4581 pub ( super ) fn new ( cache : & ' a Resource ) -> Self {
4682 ResourceRef {
47- data : cache. conversion . data . map_or ( Data :: Missing , |data| match data {
83+ data : cache. data . map_or ( Data :: Missing , |data| match data {
4884 pipeline:: Data :: Buffer => Data :: Buffer ( & cache. buffer ) ,
49- pipeline:: Data :: Binary { size } => Data :: Binary { size } ,
85+ pipeline:: Data :: TooLarge { size } => Data :: Binary { size } ,
5086 } ) ,
51- driver_choice : cache. conversion . driver ,
5287 rela_path : cache. rela_path . as_ref ( ) ,
5388 id : & cache. id ,
5489 }
@@ -118,7 +153,7 @@ pub mod set_resource {
118153
119154///
120155pub mod merge {
121- use crate :: blob:: pipeline :: DriverChoice ;
156+ use crate :: blob:: platform :: DriverChoice ;
122157 use crate :: blob:: platform:: ResourceRef ;
123158 use crate :: blob:: { builtin_driver, BuiltinDriver , Driver , Resolution } ;
124159 use bstr:: BString ;
@@ -135,6 +170,9 @@ pub mod merge {
135170 pub ancestor : ResourceRef < ' parent > ,
136171 /// The other or their side of the merge operation.
137172 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 ,
138176 }
139177
140178 #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
@@ -267,9 +305,9 @@ pub mod merge {
267305 /// Return the configured driver program for use with [`Self::prepare_external_driver()`], or `Err`
268306 /// with the built-in driver to use instead.
269307 pub fn configured_driver ( & self ) -> Result < & ' parent Driver , BuiltinDriver > {
270- match self . current . driver_choice {
308+ match self . driver_choice {
271309 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 ( ) ) ,
273311 }
274312 }
275313 }
@@ -299,14 +337,21 @@ pub mod merge {
299337
300338///
301339pub mod prepare_merge {
340+ use crate :: blob:: ResourceKind ;
341+ use bstr:: BString ;
342+
302343 /// The error returned by [Platform::prepare_merge()](super::Platform::prepare_merge_state()).
303344 #[ derive( Debug , thiserror:: Error ) ]
304345 #[ allow( missing_docs) ]
305346 pub enum Error {
306347 #[ error( "The 'current', 'ancestor' or 'other' resource for the merge operation were not set" ) ]
307348 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+ } ,
310355 }
311356}
312357
@@ -315,18 +360,44 @@ impl Platform {
315360 /// Create a new instance with a way to `filter` data from the object database and turn it into something that is merge-able.
316361 /// `filter_mode` decides how to do that specifically.
317362 /// 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 ) ) ;
319373 Platform {
374+ drivers,
320375 current : None ,
321376 ancestor : None ,
322377 other : None ,
323378 filter,
324379 filter_mode,
325380 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,
326387 }
327388 }
328389}
329390
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+
330401/// Preparation
331402impl Platform {
332403 /// Store enough information about a resource to eventually use it in a merge, where…
@@ -351,33 +422,62 @@ impl Platform {
351422 self . set_resource_inner ( id, mode, rela_path, kind, objects)
352423 }
353424
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-
364425 /// 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 > {
366431 let current = self . current . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
367432 let ancestor = self . ancestor . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
368433 let other = self . other . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
369434
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+
370472 let out = merge:: State {
371473 parent : self ,
474+ driver_choice : driver,
372475 current : ResourceRef :: new ( current) ,
373476 ancestor : ResourceRef :: new ( ancestor) ,
374477 other : ResourceRef :: new ( other) ,
375478 } ;
376479
377- match ( current. conversion . data , other. conversion . data ) {
378- ( None , None ) => Err ( prepare_merge:: Error :: CurrentOrOtherRemoved ) ,
379- ( _, _) => Ok ( out) ,
380- }
480+ Ok ( out)
381481 }
382482}
383483
@@ -430,15 +530,15 @@ impl Platform {
430530 * storage = Some ( Resource {
431531 id,
432532 rela_path : rela_path. to_owned ( ) ,
433- conversion : out,
533+ data : out,
434534 mode,
435535 buffer : buf_storage,
436536 } ) ;
437537 }
438538 Some ( storage) => {
439539 storage. id = id;
440540 storage. rela_path = rela_path. to_owned ( ) ;
441- storage. conversion = out;
541+ storage. data = out;
442542 storage. mode = mode;
443543 }
444544 } ;
0 commit comments