Skip to content

Commit e51c40b

Browse files
authored
Merge pull request #2507 from GitoxideLabs/improvements
Various Fixes
2 parents e88240b + 06a1248 commit e51c40b

File tree

7 files changed

+66
-26
lines changed

7 files changed

+66
-26
lines changed

gix-object/src/tree/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ impl EntryMode {
111111
}
112112

113113
// Happy path: space is at index 6
114-
let space_pos = if i.get(6) == Some(&b' ') {
114+
let space_pos = if i.get(6) == Some(&b' ') && i.get(5) != Some(&b' ') {
115115
for b in i.iter().take(6) {
116116
let b = b.wrapping_sub(b'0') as u16;
117117
// Not a pure octal input.

gix-object/tests/object/tree/iter.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ fn everything() -> crate::Result {
7676
Ok(())
7777
}
7878

79+
#[test]
80+
fn leading_space_in_tree_name() -> crate::Result {
81+
let oid = hex_to_id("4d5fcadc293a348e88f777dc0920f11e7d71441c");
82+
let mut buf = b"40000 leading space\0".to_vec();
83+
buf.extend_from_slice(oid.as_bytes());
84+
85+
assert_eq!(
86+
TreeRefIter::from_bytes(&buf).collect::<Result<Vec<_>, _>>()?,
87+
vec![EntryRef {
88+
mode: tree::EntryKind::Tree.into(),
89+
filename: b" leading space".as_bstr(),
90+
oid: oid.as_ref(),
91+
}]
92+
);
93+
Ok(())
94+
}
95+
7996
mod lookup_entry {
8097
use gix_object::tree::EntryKind;
8198
use utils::entry;

gix-ref/src/fullname.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,6 @@ impl Category<'_> {
166166
/// the `short_name` to create a valid fully qualified [reference name](FullName).
167167
///
168168
/// If `short_name` already contains the prefix that it would receive (and is thus a full name), no duplication will occur.
169-
///
170-
/// Note that [`Self::LocalBranch`] rejects the exact name `refs/heads/HEAD`, matching Git's branch-specific rules.
171-
/// Generic full reference handling accepts that name, however, as it is a valid reference outside of local-branch creation.
172169
pub fn to_full_name<'a>(&self, short_name: impl Into<&'a BStr>) -> Result<FullName, crate::name::Error> {
173170
let mut out: BString = self.prefix().into();
174171
let short_name = short_name.into();
@@ -189,16 +186,12 @@ impl Category<'_> {
189186
| Category::PseudoRef
190187
| Category::MainPseudoRef => short_name,
191188
};
192-
let full_name = if out.is_empty() || !partial_name.starts_with(&out) {
189+
if out.is_empty() || !partial_name.starts_with(&out) {
193190
out.extend_from_slice(partial_name);
194-
FullName::try_from(out)?
191+
FullName::try_from(out)
195192
} else {
196-
FullName::try_from(partial_name.as_bstr())?
197-
};
198-
if matches!(self, Category::LocalBranch) && full_name.as_bstr() == b"refs/heads/HEAD".as_bstr() {
199-
return Err(gix_validate::reference::name::Error::Reserved { name: full_name.into() });
193+
FullName::try_from(partial_name.as_bstr())
200194
}
201-
Ok(full_name)
202195
}
203196
}
204197

gix-ref/tests/refs/fullname.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,16 @@ fn to_full_name() -> gix_testtools::Result {
137137
}
138138

139139
#[test]
140-
fn local_branch_head_is_reserved() -> gix_testtools::Result {
141-
assert!(matches!(
142-
Category::LocalBranch.to_full_name("HEAD"),
143-
Err(gix_validate::reference::name::Error::Reserved { name }) if name == "refs/heads/HEAD"
144-
));
145-
assert!(matches!(
146-
Category::LocalBranch.to_full_name("refs/heads/HEAD"),
147-
Err(gix_validate::reference::name::Error::Reserved { name }) if name == "refs/heads/HEAD"
148-
));
140+
fn local_branch_head_is_representable_as_full_ref_name() -> gix_testtools::Result {
149141
assert_eq!(
150-
FullName::try_from("refs/heads/HEAD")?.as_bstr(),
142+
Category::LocalBranch.to_full_name("HEAD")?.as_bstr(),
151143
"refs/heads/HEAD",
152-
"generic full-name parsing still accepts this name so remote refs remain representable"
144+
"generic full-name construction accepts names that are invalid only in branch-specific contexts"
145+
);
146+
assert_eq!(
147+
Category::LocalBranch.to_full_name("refs/heads/HEAD")?.as_bstr(),
148+
"refs/heads/HEAD",
149+
"fully qualified names keep their category prefix de-duplicated"
153150
);
154151
Ok(())
155152
}

gix/src/init.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ use gix_ref::{
77
Category, FullName, Target,
88
};
99

10-
use crate::{bstr::BString, config::tree::Init, ThreadSafeRepository};
10+
use crate::{
11+
bstr::{BString, ByteSlice},
12+
config::tree::Init,
13+
ThreadSafeRepository,
14+
};
1115

1216
/// The name of the branch to use if non is configured via git configuration.
1317
///
@@ -75,12 +79,17 @@ impl ThreadSafeRepository {
7579
.string(Init::DEFAULT_BRANCH)
7680
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_BRANCH_NAME.into()));
7781
if branch_name.as_ref() != DEFAULT_BRANCH_NAME {
82+
let configured_branch_name = branch_name.into_owned();
7883
let sym_ref: FullName = Category::LocalBranch
79-
.to_full_name(branch_name.as_ref())
84+
.to_full_name(configured_branch_name.as_bstr())
8085
.map_err(|err| Error::InvalidBranchName {
81-
name: branch_name.into_owned(),
86+
name: configured_branch_name.clone(),
8287
source: err,
8388
})?;
89+
gix_validate::reference::branch_name(sym_ref.as_bstr()).map_err(|err| Error::InvalidBranchName {
90+
name: configured_branch_name,
91+
source: err,
92+
})?;
8493
let mut repo = repo.to_thread_local();
8594
let prev_write_reflog = repo.refs.write_reflog;
8695
repo.refs.write_reflog = WriteReflog::Disable;

gix/src/repository/reference.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl crate::Repository {
7373
///
7474
/// Note that this accepts any valid full reference name, including `refs/heads/HEAD`.
7575
/// Git rejects creating a local branch with that name in branch-specific code paths, but this API operates on generic
76-
/// references instead. Branch names can be validated with [`gix_ref::Category::to_full_name()`].
76+
/// references instead. Branch names can be validated with [`gix_validate::reference::branch_name()`].
7777
///
7878
/// The newly created Reference is returned.
7979
pub fn reference<Name, E>(

gix/tests/gix/init.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,30 @@ mod non_bare {
121121
Ok(())
122122
}
123123

124+
#[test]
125+
fn init_bare_rejects_reserved_fully_qualified_branch_name() -> crate::Result {
126+
let tmp = tempfile::tempdir()?;
127+
let err = gix::ThreadSafeRepository::init_opts(
128+
tmp.path(),
129+
gix::create::Kind::Bare,
130+
gix::create::Options::default(),
131+
gix::open::Options::isolated().config_overrides([
132+
"user.name=a",
133+
"user.email=b",
134+
"init.defaultBranch=refs/heads/HEAD",
135+
]),
136+
)
137+
.unwrap_err();
138+
assert!(matches!(
139+
err,
140+
gix::init::Error::InvalidBranchName {
141+
name,
142+
source: gix_validate::reference::name::Error::Reserved { name: reserved }
143+
} if name == "refs/heads/HEAD" && reserved == "refs/heads/HEAD"
144+
));
145+
Ok(())
146+
}
147+
124148
#[test]
125149
fn init_into_empty_directory_creates_a_dot_git_dir() -> crate::Result {
126150
let tmp = tempfile::tempdir()?;

0 commit comments

Comments
 (0)