Struct git_ref::FullNameRef

source ·
#[repr(transparent)]
pub struct FullNameRef(_);
Expand description

A validated and potentially partial reference name - it can safely be used for common operations.

Implementations§

Interpret this fully qualified reference name as partial name.

Examples found in repository?
src/name.rs (line 228)
227
228
229
    fn try_from(v: &'a FullName) -> Result<Self, Self::Error> {
        Ok(v.as_ref().as_partial_name())
    }
More examples
Hide additional examples
src/transaction/ext.rs (line 75)
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    fn extend_with_splits_of_symbolic_refs(
        &mut self,
        mut find: impl FnMut(&PartialNameRef) -> Option<Target>,
        mut make_entry: impl FnMut(usize, RefEdit) -> E,
    ) -> Result<(), std::io::Error> {
        let mut new_edits = Vec::new();
        let mut first = 0;
        let mut round = 1;
        loop {
            for (eid, edit) in self[first..].iter_mut().enumerate().map(|(eid, v)| (eid + first, v)) {
                let edit = edit.borrow_mut();
                if !edit.deref {
                    continue;
                };

                // we can't tell what happened and we are here because it's a non-existing ref or an invalid one.
                // In any case, we don't want the following algorithms to try dereffing it and assume they deal with
                // broken refs gracefully.
                edit.deref = false;
                if let Some(Target::Symbolic(referent)) = find(edit.name.as_ref().as_partial_name()) {
                    new_edits.push(make_entry(
                        eid,
                        match &mut edit.change {
                            Change::Delete {
                                expected: previous,
                                log: mode,
                            } => {
                                let current_mode = *mode;
                                *mode = RefLog::Only;
                                RefEdit {
                                    change: Change::Delete {
                                        expected: previous.clone(),
                                        log: current_mode,
                                    },
                                    name: referent,
                                    deref: true,
                                }
                            }
                            Change::Update { log, expected, new } => {
                                let current = std::mem::replace(
                                    log,
                                    LogChange {
                                        message: log.message.clone(),
                                        mode: RefLog::Only,
                                        force_create_reflog: log.force_create_reflog,
                                    },
                                );
                                let next = std::mem::replace(expected, PreviousValue::Any);
                                RefEdit {
                                    change: Change::Update {
                                        expected: next,
                                        new: new.clone(),
                                        log: current,
                                    },
                                    name: referent,
                                    deref: true,
                                }
                            }
                        },
                    ));
                }
            }
            if new_edits.is_empty() {
                break Ok(());
            }
            if round == 5 {
                break Err(std::io::Error::new(
                    std::io::ErrorKind::WouldBlock,
                    format!(
                        "Could not follow all splits after {} rounds, assuming reference cycle",
                        round
                    ),
                ));
            }
            round += 1;
            first = self.len();

            self.append(&mut new_edits);
        }
    }

Convert this name into the relative path identifying the reference location.

Examples found in repository?
src/store/file/find.rs (line 152)
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
    fn find_inner(
        &self,
        inbetween: &str,
        partial_name: &PartialNameRef,
        packed: Option<&packed::Buffer>,
        transform: Transform,
        path_buf: &mut BString,
    ) -> Result<Option<Reference>, Error> {
        let add_refs_prefix = matches!(transform, Transform::EnforceRefsPrefix);
        let full_name = partial_name.construct_full_name_ref(add_refs_prefix, inbetween, path_buf);
        let content_buf = self.ref_contents(full_name).map_err(|err| Error::ReadFileContents {
            source: err,
            path: self.reference_path(full_name),
        })?;

        match content_buf {
            None => {
                if let Some(packed) = packed {
                    if let Some(full_name) = packed::find::transform_full_name_for_lookup(full_name) {
                        let full_name_backing;
                        let full_name = match &self.namespace {
                            Some(namespace) => {
                                full_name_backing = namespace.to_owned().into_namespaced_name(full_name);
                                full_name_backing.as_ref()
                            }
                            None => full_name,
                        };
                        if let Some(packed_ref) = packed.try_find_full_name(full_name)? {
                            let mut res: Reference = packed_ref.into();
                            if let Some(namespace) = &self.namespace {
                                res.strip_namespace(namespace);
                            }
                            return Ok(Some(res));
                        };
                    }
                }
                Ok(None)
            }
            Some(content) => Ok(Some(
                loose::Reference::try_from_path(full_name.to_owned(), &content)
                    .map(Into::into)
                    .map(|mut r: Reference| {
                        if let Some(namespace) = &self.namespace {
                            r.strip_namespace(namespace);
                        }
                        r
                    })
                    .map_err(|err| Error::ReferenceCreation {
                        source: err,
                        relative_path: full_name.to_path().to_owned(),
                    })?,
            )),
        }
    }

Return ourselves as byte string which is a valid refname

Examples found in repository?
src/fullname.rs (line 64)
63
64
65
    fn from(value: &'a FullNameRef) -> Self {
        FullName(value.as_bstr().into())
    }
More examples
Hide additional examples
src/namespace.rs (line 31)
30
31
32
33
    pub(crate) fn into_namespaced_name(mut self, name: &FullNameRef) -> FullName {
        self.0.push_str(name.as_bstr());
        FullName(self.0)
    }
src/target.rs (line 118)
115
116
117
118
119
120
121
    fn eq(&self, other: &TargetRef<'a>) -> bool {
        match (self, other) {
            (Target::Peeled(lhs), TargetRef::Peeled(rhs)) => lhs == rhs,
            (Target::Symbolic(lhs), TargetRef::Symbolic(rhs)) => lhs.as_bstr() == rhs.as_bstr(),
            _ => false,
        }
    }
src/store/file/find.rs (line 201)
196
197
198
199
200
201
202
203
204
205
206
207
    pub(crate) fn reference_path_with_base<'b>(&self, name: &'b FullNameRef) -> (Cow<'_, Path>, Cow<'b, Path>) {
        let (base, name) = self.to_base_dir_and_relative_name(name, false);
        (
            base,
            match &self.namespace {
                None => git_path::to_native_path_on_windows(name.as_bstr()),
                Some(namespace) => {
                    git_path::to_native_path_on_windows(namespace.to_owned().into_namespaced_name(name).into_inner())
                }
            },
        )
    }
src/store/packed/find.rs (line 42)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    pub(crate) fn try_find_full_name(&self, name: &FullNameRef) -> Result<Option<packed::Reference<'_>>, Error> {
        match self.binary_search_by(name.as_bstr()) {
            Ok(line_start) => Ok(Some(
                packed::decode::reference::<()>(&self.as_ref()[line_start..])
                    .map_err(|_| Error::Parse)?
                    .1,
            )),
            Err((parse_failure, _)) => {
                if parse_failure {
                    Err(Error::Parse)
                } else {
                    Ok(None)
                }
            }
        }
    }

    /// Find a reference with the given `name` and return it.
    pub fn find<'a, Name, E>(&self, name: Name) -> Result<packed::Reference<'_>, existing::Error>
    where
        Name: TryInto<&'a PartialNameRef, Error = E>,
        Error: From<E>,
    {
        match self.try_find(name) {
            Ok(Some(r)) => Ok(r),
            Ok(None) => Err(existing::Error::NotFound),
            Err(err) => Err(existing::Error::Find(err)),
        }
    }

    /// Perform a binary search where `Ok(pos)` is the beginning of the line that matches `name` perfectly and `Err(pos)`
    /// is the beginning of the line at which `name` could be inserted to still be in sort order.
    pub(in crate::store_impl::packed) fn binary_search_by(&self, full_name: &BStr) -> Result<usize, (bool, usize)> {
        let a = self.as_ref();
        let search_start_of_record = |ofs: usize| {
            a[..ofs]
                .rfind(b"\n")
                .and_then(|pos| {
                    let candidate = pos + 1;
                    a.get(candidate).and_then(|b| {
                        if *b == b'^' {
                            a[..pos].rfind(b"\n").map(|pos| pos + 1)
                        } else {
                            Some(candidate)
                        }
                    })
                })
                .unwrap_or(0)
        };
        let mut encountered_parse_failure = false;
        a.binary_search_by_key(&full_name.as_ref(), |b: &u8| {
            let ofs = b as *const u8 as usize - a.as_ptr() as usize;
            let line = &a[search_start_of_record(ofs)..];
            packed::decode::reference::<()>(line)
                .map(|(_rest, r)| r.name.as_bstr().as_ref())
                .map_err(|err| {
                    encountered_parse_failure = true;
                    err
                })
                .unwrap_or(&[])
        })
        .map(search_start_of_record)
        .map_err(|pos| (encountered_parse_failure, search_start_of_record(pos)))
    }
src/store/file/loose/reflog.rs (line 192)
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
        pub(in crate::store_impl::file) fn reflog_base_and_relative_path<'a>(
            &self,
            name: &'a FullNameRef,
        ) -> (PathBuf, Cow<'a, Path>) {
            let is_reflog = true;
            let (base, name) = self.to_base_dir_and_relative_name(name, is_reflog);
            (
                base.join("logs"),
                match &self.namespace {
                    None => git_path::to_native_path_on_windows(name.as_bstr()),
                    Some(namespace) => git_path::to_native_path_on_windows(
                        namespace.to_owned().into_namespaced_name(name).into_inner(),
                    ),
                },
            )
        }

Strip well-known prefixes from the name and return it.

If there is no such prefix, the original name is returned.

Examples found in repository?
src/fullname.rs (line 196)
195
196
197
    pub fn shorten(&self) -> &BStr {
        self.as_ref().shorten()
    }

Classify this name, or return None if it’s unclassified.

Examples found in repository?
src/fullname.rs (line 201)
200
201
202
    pub fn category(&self) -> Option<crate::Category<'_>> {
        self.as_ref().category()
    }
More examples
Hide additional examples
src/store/file/transaction/prepare.rs (line 418)
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
fn possibly_adjust_name_for_prefixes(name: &FullNameRef) -> Option<FullName> {
    match name.category_and_short_name() {
        Some((c, sn)) => {
            use crate::Category::*;
            let sn = FullNameRef::new_unchecked(sn);
            match c {
                Bisect | Rewritten | WorktreePrivate | LinkedPseudoRef { .. } | PseudoRef | MainPseudoRef => None,
                Tag | LocalBranch | RemoteBranch | Note => name.into(),
                MainRef | LinkedRef { .. } => sn
                    .category()
                    .map_or(false, |cat| !cat.is_worktree_private())
                    .then(|| sn),
            }
            .map(|n| n.to_owned())
        }
        None => Some(name.to_owned()), // allow (uncategorized/very special) refs to be packed
    }
}
src/store/file/find.rs (line 179)
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    pub(crate) fn to_base_dir_and_relative_name<'a>(
        &self,
        name: &'a FullNameRef,
        is_reflog: bool,
    ) -> (Cow<'_, Path>, &'a FullNameRef) {
        let commondir = self.common_dir_resolved();
        let linked_git_dir =
            |worktree_name: &BStr| commondir.join("worktrees").join(git_path::from_bstr(worktree_name));
        name.category_and_short_name()
            .and_then(|(c, sn)| {
                use crate::Category::*;
                let sn = FullNameRef::new_unchecked(sn);
                Some(match c {
                    LinkedPseudoRef { name: worktree_name } => is_reflog
                        .then(|| (linked_git_dir(worktree_name).into(), sn))
                        .unwrap_or((commondir.into(), name)),
                    Tag | LocalBranch | RemoteBranch | Note => (commondir.into(), name),
                    MainRef | MainPseudoRef => (commondir.into(), sn),
                    LinkedRef { name: worktree_name } => sn
                        .category()
                        .map_or(false, |cat| cat.is_worktree_private())
                        .then(|| {
                            if is_reflog {
                                (linked_git_dir(worktree_name).into(), sn)
                            } else {
                                (commondir.into(), name)
                            }
                        })
                        .unwrap_or((commondir.into(), sn)),
                    PseudoRef | Bisect | Rewritten | WorktreePrivate => return None,
                })
            })
            .unwrap_or((self.git_dir.as_path().into(), name))
    }

Classify this name, or return None if it’s unclassified. If Some, the shortened name is returned as well.

Examples found in repository?
src/fullname.rs (line 94)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    pub fn shorten(&self) -> &BStr {
        self.category_and_short_name()
            .map(|(_, short)| short)
            .unwrap_or_else(|| self.0.as_bstr())
    }

    /// Classify this name, or return `None` if it's unclassified.
    pub fn category(&self) -> Option<Category<'_>> {
        self.category_and_short_name().map(|(cat, _)| cat)
    }

    /// Classify this name, or return `None` if it's unclassified. If `Some`,
    /// the shortened name is returned as well.
    pub fn category_and_short_name(&self) -> Option<(Category<'_>, &BStr)> {
        let name = self.0.as_bstr();
        for category in &[Category::Tag, Category::LocalBranch, Category::RemoteBranch] {
            if let Some(shortened) = name.strip_prefix(category.prefix().as_ref()) {
                return Some((*category, shortened.as_bstr()));
            }
        }

        for category in &[
            Category::Note,
            Category::Bisect,
            Category::WorktreePrivate,
            Category::Rewritten,
        ] {
            if name.starts_with(category.prefix().as_ref()) {
                return Some((
                    *category,
                    name.strip_prefix(b"refs/")
                        .expect("we checked for refs/* above")
                        .as_bstr(),
                ));
            }
        }

        if is_pseudo_ref(name) {
            Some((Category::PseudoRef, name))
        } else if let Some(shortened) = name.strip_prefix(Category::MainPseudoRef.prefix().as_ref()) {
            if shortened.starts_with_str("refs/") {
                (Category::MainRef, shortened.as_bstr()).into()
            } else {
                is_pseudo_ref(shortened).then(|| (Category::MainPseudoRef, shortened.as_bstr()))
            }
        } else if let Some(shortened_with_worktree_name) =
            name.strip_prefix(Category::LinkedPseudoRef { name: "".into() }.prefix().as_ref())
        {
            let (name, shortened) = shortened_with_worktree_name.find_byte(b'/').map(|pos| {
                (
                    shortened_with_worktree_name[..pos].as_bstr(),
                    shortened_with_worktree_name[pos + 1..].as_bstr(),
                )
            })?;
            if shortened.starts_with_str("refs/") {
                (Category::LinkedRef { name }, shortened.as_bstr()).into()
            } else {
                is_pseudo_ref(shortened).then(|| (Category::LinkedPseudoRef { name }, shortened.as_bstr()))
            }
        } else {
            None
        }
    }
}

impl FullName {
    /// Convert this name into the relative path, lossily, identifying the reference location relative to a repository
    pub fn to_path(&self) -> &Path {
        git_path::from_byte_slice(&self.0)
    }

    /// Dissolve this instance and return the buffer.
    pub fn into_inner(self) -> BString {
        self.0
    }

    /// Return ourselves as byte string which is a valid refname
    pub fn as_bstr(&self) -> &BStr {
        self.0.as_bstr()
    }

    /// Modify ourself so that we use `namespace` as prefix, if it is not yet in the `namespace`
    pub fn prefix_namespace(&mut self, namespace: &Namespace) -> &mut Self {
        if !self.0.starts_with_str(&namespace.0) {
            self.0.insert_str(0, &namespace.0);
        }
        self
    }

    /// Strip the given `namespace` off the beginning of this name, if it is in this namespace.
    pub fn strip_namespace(&mut self, namespace: &Namespace) -> &mut Self {
        if self.0.starts_with_str(&namespace.0) {
            let prev_len = self.0.len();
            self.0.copy_within(namespace.0.len().., 0);
            self.0.resize(prev_len - namespace.0.len(), 0);
        }
        self
    }

    /// Strip well-known prefixes from the name and return it.
    ///
    /// If there is no such prefix, the original name is returned.
    pub fn shorten(&self) -> &BStr {
        self.as_ref().shorten()
    }

    /// Classify this name, or return `None` if it's unclassified.
    pub fn category(&self) -> Option<crate::Category<'_>> {
        self.as_ref().category()
    }

    /// Classify this name, or return `None` if it's unclassified. If `Some`,
    /// the shortened name is returned as well.
    pub fn category_and_short_name(&self) -> Option<(crate::Category<'_>, &BStr)> {
        self.as_ref().category_and_short_name()
    }
More examples
Hide additional examples
src/store/packed/find.rs (line 143)
142
143
144
145
146
147
148
149
150
151
152
153
154
pub(crate) fn transform_full_name_for_lookup(name: &FullNameRef) -> Option<&FullNameRef> {
    match name.category_and_short_name() {
        Some((c, sn)) => {
            use crate::Category::*;
            Some(match c {
                MainRef | LinkedRef { .. } => FullNameRef::new_unchecked(sn),
                Tag | RemoteBranch | LocalBranch | Bisect | Rewritten | Note => name,
                MainPseudoRef | PseudoRef | LinkedPseudoRef { .. } | WorktreePrivate => return None,
            })
        }
        None => Some(name),
    }
}
src/store/file/transaction/prepare.rs (line 410)
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
fn possibly_adjust_name_for_prefixes(name: &FullNameRef) -> Option<FullName> {
    match name.category_and_short_name() {
        Some((c, sn)) => {
            use crate::Category::*;
            let sn = FullNameRef::new_unchecked(sn);
            match c {
                Bisect | Rewritten | WorktreePrivate | LinkedPseudoRef { .. } | PseudoRef | MainPseudoRef => None,
                Tag | LocalBranch | RemoteBranch | Note => name.into(),
                MainRef | LinkedRef { .. } => sn
                    .category()
                    .map_or(false, |cat| !cat.is_worktree_private())
                    .then(|| sn),
            }
            .map(|n| n.to_owned())
        }
        None => Some(name.to_owned()), // allow (uncategorized/very special) refs to be packed
    }
}
src/store/file/find.rs (line 168)
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    pub(crate) fn to_base_dir_and_relative_name<'a>(
        &self,
        name: &'a FullNameRef,
        is_reflog: bool,
    ) -> (Cow<'_, Path>, &'a FullNameRef) {
        let commondir = self.common_dir_resolved();
        let linked_git_dir =
            |worktree_name: &BStr| commondir.join("worktrees").join(git_path::from_bstr(worktree_name));
        name.category_and_short_name()
            .and_then(|(c, sn)| {
                use crate::Category::*;
                let sn = FullNameRef::new_unchecked(sn);
                Some(match c {
                    LinkedPseudoRef { name: worktree_name } => is_reflog
                        .then(|| (linked_git_dir(worktree_name).into(), sn))
                        .unwrap_or((commondir.into(), name)),
                    Tag | LocalBranch | RemoteBranch | Note => (commondir.into(), name),
                    MainRef | MainPseudoRef => (commondir.into(), sn),
                    LinkedRef { name: worktree_name } => sn
                        .category()
                        .map_or(false, |cat| cat.is_worktree_private())
                        .then(|| {
                            if is_reflog {
                                (linked_git_dir(worktree_name).into(), sn)
                            } else {
                                (commondir.into(), name)
                            }
                        })
                        .unwrap_or((commondir.into(), sn)),
                    PseudoRef | Bisect | Rewritten | WorktreePrivate => return None,
                })
            })
            .unwrap_or((self.git_dir.as_path().into(), name))
    }

Return the file name portion of a full name, for instance main if the full name was refs/heads/main.

Trait Implementations§

Converts this type into a shared reference of the (usually inferred) input type.
Immutably borrows from an owned value. Read more
Formats the value using the given formatter. Read more
Converts to this type from the input type.
Converts to this type from the input type.
Converts to this type from the input type.
Feeds this value into the given Hasher. Read more
This method returns an Ordering between self and other. Read more
This method tests for self and other values to be equal, and is used by ==.
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
This method returns an ordering between self and other values if one exists. Read more
This method tests less than (for self and other) and is used by the < operator. Read more
This method tests less than or equal to (for self and other) and is used by the <= operator. Read more
This method tests greater than (for self and other) and is used by the > operator. Read more
This method tests greater than or equal to (for self and other) and is used by the >= operator. Read more
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more