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,8 @@ 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+ pub driver_choice : DriverChoice ,
138175 }
139176
140177 #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
@@ -267,9 +304,9 @@ pub mod merge {
267304 /// Return the configured driver program for use with [`Self::prepare_external_driver()`], or `Err`
268305 /// with the built-in driver to use instead.
269306 pub fn configured_driver ( & self ) -> Result < & ' parent Driver , BuiltinDriver > {
270- match self . current . driver_choice {
307+ match self . driver_choice {
271308 DriverChoice :: BuiltIn ( builtin) => Err ( builtin) ,
272- DriverChoice :: Index ( idx) => self . parent . filter . drivers . get ( idx) . ok_or ( BuiltinDriver :: default ( ) ) ,
309+ DriverChoice :: Index ( idx) => self . parent . drivers . get ( idx) . ok_or ( BuiltinDriver :: default ( ) ) ,
273310 }
274311 }
275312 }
@@ -299,14 +336,21 @@ pub mod merge {
299336
300337///
301338pub mod prepare_merge {
339+ use crate :: blob:: ResourceKind ;
340+ use bstr:: BString ;
341+
302342 /// The error returned by [Platform::prepare_merge()](super::Platform::prepare_merge_state()).
303343 #[ derive( Debug , thiserror:: Error ) ]
304344 #[ allow( missing_docs) ]
305345 pub enum Error {
306346 #[ error( "The 'current', 'ancestor' or 'other' resource for the merge operation were not set" ) ]
307347 UnsetResource ,
308- #[ error( "Tried to merge 'current' and 'other' where at least one of them is removed" ) ]
309- CurrentOrOtherRemoved ,
348+ #[ error( "Failed to obtain attributes for {kind:?} resource at '{rela_path}'" ) ]
349+ Attributes {
350+ rela_path : BString ,
351+ kind : ResourceKind ,
352+ source : std:: io:: Error ,
353+ } ,
310354 }
311355}
312356
@@ -315,18 +359,44 @@ impl Platform {
315359 /// Create a new instance with a way to `filter` data from the object database and turn it into something that is merge-able.
316360 /// `filter_mode` decides how to do that specifically.
317361 /// 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 {
362+ /// `drivers` are the list of available merge drivers that individual paths can refer to by means of git attributes.
363+ /// `options` further configure the operation.
364+ pub fn new (
365+ filter : Pipeline ,
366+ filter_mode : pipeline:: Mode ,
367+ attr_stack : gix_worktree:: Stack ,
368+ mut drivers : Vec < super :: Driver > ,
369+ options : Options ,
370+ ) -> Self {
371+ drivers. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
319372 Platform {
373+ drivers,
320374 current : None ,
321375 ancestor : None ,
322376 other : None ,
323377 filter,
324378 filter_mode,
325379 attr_stack,
380+ attrs : {
381+ let mut out = attributes:: search:: Outcome :: default ( ) ;
382+ out. initialize_with_selection ( & Default :: default ( ) , Some ( "merge" ) ) ;
383+ out
384+ } ,
385+ options,
326386 }
327387 }
328388}
329389
390+ /// Access
391+ impl Platform {
392+ /// Return all drivers that this instance was initialized with.
393+ ///
394+ /// They are sorted by [`name`](super::Driver::name) to support binary searches.
395+ pub fn drivers ( & self ) -> & [ super :: Driver ] {
396+ & self . drivers
397+ }
398+ }
399+
330400/// Preparation
331401impl Platform {
332402 /// Store enough information about a resource to eventually use it in a merge, where…
@@ -351,33 +421,62 @@ impl Platform {
351421 self . set_resource_inner ( id, mode, rela_path, kind, objects)
352422 }
353423
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-
364424 /// 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 > {
425+ /// Note that no additional validation is performed here to facilitate inspection.
426+ pub fn prepare_merge_state (
427+ & mut self ,
428+ objects : & impl gix_object:: Find ,
429+ ) -> Result < merge:: State < ' _ > , prepare_merge:: Error > {
366430 let current = self . current . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
367431 let ancestor = self . ancestor . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
368432 let other = self . other . as_ref ( ) . ok_or ( prepare_merge:: Error :: UnsetResource ) ?;
369433
434+ let entry = self
435+ . attr_stack
436+ . at_entry ( current. rela_path . as_bstr ( ) , None , objects)
437+ . map_err ( |err| prepare_merge:: Error :: Attributes {
438+ source : err,
439+ kind : ResourceKind :: CurrentOrOurs ,
440+ rela_path : current. rela_path . clone ( ) ,
441+ } ) ?;
442+ entry. matching_attributes ( & mut self . attrs ) ;
443+ let attr = self . attrs . iter_selected ( ) . next ( ) . expect ( "pre-initialized with 'diff'" ) ;
444+ let driver = match attr. assignment . state {
445+ attributes:: StateRef :: Set => DriverChoice :: BuiltIn ( BuiltinDriver :: Text ) ,
446+ attributes:: StateRef :: Unset => DriverChoice :: BuiltIn ( BuiltinDriver :: Binary ) ,
447+ attributes:: StateRef :: Value ( _) | attributes:: StateRef :: Unspecified => {
448+ let name = match attr. assignment . state {
449+ attributes:: StateRef :: Value ( name) => Some ( name. as_bstr ( ) ) ,
450+ attributes:: StateRef :: Unspecified => {
451+ self . options . default_driver . as_ref ( ) . map ( |name| name. as_bstr ( ) )
452+ }
453+ _ => unreachable ! ( "only value and unspecified are possible here" ) ,
454+ } ;
455+ name. and_then ( |name| {
456+ self . drivers
457+ . binary_search_by ( |d| d. name . as_bstr ( ) . cmp ( name) )
458+ . ok ( )
459+ . map ( DriverChoice :: Index )
460+ . or_else ( || {
461+ name. to_str ( )
462+ . ok ( )
463+ . and_then ( BuiltinDriver :: by_name)
464+ . map ( DriverChoice :: BuiltIn )
465+ } )
466+ } )
467+ . unwrap_or_default ( )
468+ }
469+ } ;
470+
370471 let out = merge:: State {
371472 parent : self ,
473+ driver_choice : driver,
372474 current : ResourceRef :: new ( current) ,
373475 ancestor : ResourceRef :: new ( ancestor) ,
374476 other : ResourceRef :: new ( other) ,
375477 } ;
376478
377- match ( current. conversion . data , other. conversion . data ) {
378- ( None , None ) => Err ( prepare_merge:: Error :: CurrentOrOtherRemoved ) ,
379- ( _, _) => Ok ( out) ,
380- }
479+ Ok ( out)
381480 }
382481}
383482
@@ -430,15 +529,15 @@ impl Platform {
430529 * storage = Some ( Resource {
431530 id,
432531 rela_path : rela_path. to_owned ( ) ,
433- conversion : out,
532+ data : out,
434533 mode,
435534 buffer : buf_storage,
436535 } ) ;
437536 }
438537 Some ( storage) => {
439538 storage. id = id;
440539 storage. rela_path = rela_path. to_owned ( ) ;
441- storage. conversion = out;
540+ storage. data = out;
442541 storage. mode = mode;
443542 }
444543 } ;
0 commit comments