This means tree entries are compared as if they had a / appended to it.
Unified · Split
| @@ -72,7 +72,7 @@ pub struct EntryRef<'a> { · 71 unmodified lines | ||
| 72 | 72 | pub filename: &'a BStr, |
| 73 | 73 | /// The id of the object representing the entry. |
| 74 | 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 | 76 | #[cfg_attr(feature = "serde", serde(borrow))] |
| 77 | 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 | |
| 86 | 86 | impl<'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. | |
| 88 | − /// Doing it like this is needed for compatibility with older, potentially broken(?) trees. | |
| 89 | − fn cmp(&self, other: &Self) -> Ordering { | |
| 90 | − let len = self.filename.len().min(other.filename.len()); | |
| 91 | − self.filename[..len].cmp(&other.filename[..len]) | |
| 87 | + fn cmp(&self, b: &Self) -> Ordering { | |
| 88 | + let a = self; | |
| 89 | + let common = a.filename.len().min(b.filename.len()); | |
| 90 | + a.filename[..common].cmp(&b.filename[..common]).then_with(|| { | |
| 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 | |
| 113 | 116 | impl Ord for Entry { |
| 114 | − /// Entries compare by the common portion of the filename. This is critical for proper functioning of algorithms working on trees. | |
| 115 | − fn cmp(&self, other: &Self) -> Ordering { | |
| 116 | − let common_len = self.filename.len().min(other.filename.len()); | |
| 117 | − self.filename[..common_len] | |
| 118 | − .cmp(&other.filename[..common_len]) | |
| 119 | − .then_with(|| self.filename.len().cmp(&other.filename.len())) | |
| 117 | + fn cmp(&self, b: &Self) -> Ordering { | |
| 118 | + let a = self; | |
| 119 | + let common = a.filename.len().min(b.filename.len()); | |
| 120 | + a.filename[..common].cmp(&b.filename[..common]).then_with(|| { | |
| 121 | + let a = a.filename.get(common).or_else(|| a.mode.is_tree().then_some(&b'/')); | |
| 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 | |
| @@ -53,16 +53,16 @@ impl crate::WriteTo for Tree { · 52 unmodified lines | ||
| 53 | 53 | Ok(()) |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | + fn kind(&self) -> Kind { | |
| 57 | + Kind::Tree | |
| 58 | + } | |
| 59 | + | |
| 56 | 60 | fn size(&self) -> usize { |
| 57 | 61 | self.entries |
| 58 | 62 | .iter() |
| 59 | 63 | .map(|Entry { mode, filename, oid }| mode.as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len()) |
| 60 | 64 | .sum() |
| 61 | 65 | } |
| 62 | − | |
| 63 | − fn kind(&self) -> Kind { | |
| 64 | − Kind::Tree | |
| 65 | − } | |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | /// Serialization |
| @@ -96,6 +96,10 @@ impl<'a> crate::WriteTo for TreeRef<'a> { · 27 unmodified lines | ||
| 96 | 96 | Ok(()) |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | + fn kind(&self) -> Kind { | |
| 100 | + Kind::Tree | |
| 101 | + } | |
| 102 | + | |
| 99 | 103 | fn size(&self) -> usize { |
| 100 | 104 | self.entries |
| 101 | 105 | .iter() |
| @@ -104,8 +108,4 @@ impl<'a> crate::WriteTo for TreeRef<'a> { · 2 unmodified lines | ||
| 104 | 108 | }) |
| 105 | 109 | .sum() |
| 106 | 110 | } |
| 107 | − | |
| 108 | − fn kind(&self) -> Kind { | |
| 109 | − Kind::Tree | |
| 110 | − } | |
| 111 | 111 | } |
| @@ -0,0 +1,3 @@ | ||
| 1 | +version https://git-lfs.github.com/spec/v1 | |
| 2 | +oid sha256:9d0ebc472023fffe49d71303e5f41f8c6ca24e28d283468f2426791c6286ecf7 | |
| 3 | +size 10240 | |
| @@ -0,0 +1,19 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +set -eu -o pipefail | |
| 3 | + | |
| 4 | +function 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 | + | |
| 12 | +git init -q | |
| 13 | +mkdir file | |
| 14 | +touch bin bin.d file.to file.toml file.toml.bin file0 file/a | |
| 15 | + | |
| 16 | +git add . | |
| 17 | +git commit -m "c1" | |
| 18 | + | |
| 19 | +baseline @^{tree} tree | |
| @@ -130,6 +130,29 @@ mod from_bytes { · 129 unmodified lines | ||
| 130 | 130 | } |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | +mod 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 | + | |
| 133 | 156 | mod entry_mode { |
| 134 | 157 | use gix_object::tree::EntryMode; |
| 135 | 158 | |