History

fix: sorting of tree entries is now according to specificiation.

This means tree entries are compared as if they had a / appended to it.

Sebastian Thiel committed 3 years ago

Commit ca379151 parent d92a5885 files · +70 −20Browse files

Unified · Split

5 files
  1. gix-object/src/tree/mod.rs +17 −12
  2. gix-object/src/tree/write.rs +8 −8
  3. gix-object/tests/fixtures/generated-archives/make_trees.tar.xz +3 −0
  4. gix-object/tests/fixtures/make_trees.sh +19 −0
  5. gix-object/tests/tree/mod.rs +23 −0
gix-object/src/tree/mod.rs+17−12
@@ -72,7 +72,7 @@ pub struct EntryRef<'a> { · 71 unmodified lines
72 pub filename: &'a BStr,72 pub filename: &'a BStr,
73 /// The id of the object representing the entry.73 /// The id of the object representing the entry.
74 // TODO: figure out how these should be called. id or oid? It's inconsistent around the codebase.74 // TODO: figure out how these should be called. id or oid? It's inconsistent around the codebase.
75 // Answer: make it 'id', as in `git2`75 // Answer: make it 'id', as in `git2`
76 #[cfg_attr(feature = "serde", serde(borrow))]76 #[cfg_attr(feature = "serde", serde(borrow))]
77 pub oid: &'a gix_hash::oid,77 pub oid: &'a gix_hash::oid,
78}78}
@@ -84,11 +84,14 @@ impl<'a> PartialOrd for EntryRef<'a> { · 5 unmodified lines
84}84}
85 85
86impl<'a> Ord for EntryRef<'a> {86impl<'a> Ord for EntryRef<'a> {
87 /// Entries compare by the common portion of the filename. This is critical for proper functioning of algorithms working on trees.87 fn cmp(&self, b: &Self) -> Ordering {
88 /// Doing it like this is needed for compatibility with older, potentially broken(?) trees.88 let a = self;
89 fn cmp(&self, other: &Self) -> Ordering {89 let common = a.filename.len().min(b.filename.len());
90 let len = self.filename.len().min(other.filename.len());90 a.filename[..common].cmp(&b.filename[..common]).then_with(|| {
91 self.filename[..len].cmp(&other.filename[..len])91 let a = a.filename.get(common).or_else(|| a.mode.is_tree().then_some(&b'/'));
92 let b = b.filename.get(common).or_else(|| b.mode.is_tree().then_some(&b'/'));
93 a.cmp(&b)
94 })
92 }95 }
93}96}
94 97
@@ -111,12 +114,14 @@ impl PartialOrd for Entry { · 16 unmodified lines
111}114}
112 115
113impl Ord for Entry {116impl Ord for Entry {
114 /// Entries compare by the common portion of the filename. This is critical for proper functioning of algorithms working on trees.117 fn cmp(&self, b: &Self) -> Ordering {
115 fn cmp(&self, other: &Self) -> Ordering {118 let a = self;
116 let common_len = self.filename.len().min(other.filename.len());119 let common = a.filename.len().min(b.filename.len());
117 self.filename[..common_len]120 a.filename[..common].cmp(&b.filename[..common]).then_with(|| {
118 .cmp(&other.filename[..common_len])121 let a = a.filename.get(common).or_else(|| a.mode.is_tree().then_some(&b'/'));
119 .then_with(|| self.filename.len().cmp(&other.filename.len()))122 let b = b.filename.get(common).or_else(|| b.mode.is_tree().then_some(&b'/'));
123 a.cmp(&b)
124 })
120 }125 }
121}126}
122 127
gix-object/src/tree/write.rs+8−8
@@ -53,16 +53,16 @@ impl crate::WriteTo for Tree { · 52 unmodified lines
53 Ok(())53 Ok(())
54 }54 }
55 55
56 fn kind(&self) -> Kind {
57 Kind::Tree
58 }
59
56 fn size(&self) -> usize {60 fn size(&self) -> usize {
57 self.entries61 self.entries
58 .iter()62 .iter()
59 .map(|Entry { mode, filename, oid }| mode.as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len())63 .map(|Entry { mode, filename, oid }| mode.as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len())
60 .sum()64 .sum()
61 }65 }
62
63 fn kind(&self) -> Kind {
64 Kind::Tree
65 }
66}66}
67 67
68/// Serialization68/// Serialization
@@ -96,6 +96,10 @@ impl<'a> crate::WriteTo for TreeRef<'a> { · 27 unmodified lines
96 Ok(())96 Ok(())
97 }97 }
98 98
99 fn kind(&self) -> Kind {
100 Kind::Tree
101 }
102
99 fn size(&self) -> usize {103 fn size(&self) -> usize {
100 self.entries104 self.entries
101 .iter()105 .iter()
@@ -104,8 +108,4 @@ impl<'a> crate::WriteTo for TreeRef<'a> { · 2 unmodified lines
104 })108 })
105 .sum()109 .sum()
106 }110 }
107
108 fn kind(&self) -> Kind {
109 Kind::Tree
110 }
111}111}
gix-object/tests/fixtures/generated-archives/make_trees.tar.xz+3−0
@@ -0,0 +1,3 @@
1version https://git-lfs.github.com/spec/v1
2oid sha256:9d0ebc472023fffe49d71303e5f41f8c6ca24e28d283468f2426791c6286ecf7
3size 10240
gix-object/tests/fixtures/make_trees.sh+19−0
@@ -0,0 +1,19 @@
1#!/bin/bash
2set -eu -o pipefail
3
4function baseline() {
5 local revspec=${1:?First argument is the revspec of the object to create a baseline for}
6 local basename=${2:?Second argument is the name of the baseline file}
7 local baseline_file="$basename.baseline"
8 git rev-parse "$revspec" | git cat-file --batch | tail -n +2 > "$baseline_file"
9 truncate -s "$(($(stat -c '%s' "$baseline_file")-1))" "$baseline_file"
10}
11
12git init -q
13mkdir file
14touch bin bin.d file.to file.toml file.toml.bin file0 file/a
15
16git add .
17git commit -m "c1"
18
19baseline @^{tree} tree
gix-object/tests/tree/mod.rs+23−0
@@ -130,6 +130,29 @@ mod from_bytes { · 129 unmodified lines
130 }130 }
131}131}
132 132
133mod entries {
134 use gix_object::{Tree, TreeRef};
135
136 #[test]
137 fn sort_order_is_correct() -> crate::Result {
138 let root = gix_testtools::scripted_fixture_read_only("make_trees.sh")?;
139 let input = std::fs::read(root.join("tree.baseline"))?;
140
141 let mut tree = TreeRef::from_bytes(&input)?;
142 let expected = tree.entries.clone();
143
144 tree.entries.sort();
145 assert_eq!(tree.entries, expected);
146
147 let mut tree: Tree = tree.into();
148 let expected = tree.entries.clone();
149 tree.entries.sort();
150
151 assert_eq!(tree.entries, expected);
152 Ok(())
153 }
154}
155
133mod entry_mode {156mod entry_mode {
134 use gix_object::tree::EntryMode;157 use gix_object::tree::EntryMode;
135 158

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t