Skip to content

Commit

Permalink
Merge branch 'gix-clone'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Nov 2, 2022
2 parents 2514334 + 3890f1a commit def53b3
Show file tree
Hide file tree
Showing 115 changed files with 3,642 additions and 1,300 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ jobs:
- name: Setup dependencies
run:
sudo apt-get install tree
- run: git config --global protocol.file.allow always # workaround for https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586
- name: test
env:
CI: true
Expand Down
5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ git-repository = { version = "^0.25.0", path = "git-repository", default-feature
git-transport-for-configuration-only = { package = "git-transport", optional = true, version = "^0.21.0", path = "git-transport" }

clap = { version = "3.2.5", features = ["derive", "cargo"] }
prodash = { version = "20.2.0", optional = true, default-features = false }
prodash = { version = "21", optional = true, default-features = false }
atty = { version = "0.2.14", optional = true, default-features = false }
env_logger = { version = "0.9.0", default-features = false }
crosstermion = { version = "0.10.1", optional = true, default-features = false }
Expand Down
5 changes: 3 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ A bunch of notes collected to keep track of what's needed to eventually support

## `Options` vs `Context`

- Use `Options` whenever there is something to configure in terms of branching behaviour.
- Use `Context` when potential optional data is required to perform an operation at all. See `git_config::path::Context` as reference.
- Use `Options` whenever there is something to configure in terms of branching behaviour. It can be defaulted, and if it can't these fields should be parameters.
- Use `Context` when potential optional data is required to perform an operation at all. See `git_config::path::Context` as reference. It can't be defaulted and the
fields could also be parameters.

## Examples, Experiments, Porcelain CLI and Plumbing CLI - which does what?

Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ Please see _'Development Status'_ for a listing of all crates and their capabili
* [x] **ref-map** - show how remote references relate to their local tracking branches as mapped by refspecs.
* [x] **fetch** - fetch the current remote or the given one, optionally just as dry-run.
* **clone**
* [ ] initialize a new **bare** repository and fetch all objects.
* [ ] initialize a new repository, fetch all objects and checkout the main worktree.
* [x] initialize a new **bare** repository and fetch all objects.
* [x] initialize a new repository, fetch all objects and checkout the main worktree.
* **credential**
* [x] **fill/approve/reject** - The same as `git credential`, but implemented in Rust, calling helpers only when from trusted configuration.
* **free** - no git repository necessary
Expand Down Expand Up @@ -313,11 +313,13 @@ For additional details, also take a look at the [collaboration guide].
Provide a CLI to for the most basic user journey:

* [x] initialize a repository
* [x] fetch
* [ ] and update worktree
* clone a repository
- [ ] bare
- [ ] with working tree
* [ ] create a commit
* [ ] add a remote
* [ ] create a commit after adding worktree files
* [x] add a remote
* [ ] push
* [x] create (thin) pack

Expand Down
13 changes: 6 additions & 7 deletions crate-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Check out the [performance discussion][git-diff-performance] as well.
* There are various ways to generate a patch from two blobs.
* [ ] any
* **lines**
* [ ] Simple line-by-line diffs powered by the `similar` crate.
* [x] Simple line-by-line diffs powered by the `imara-diff` crate.
* diffing, merging, working with hunks of data
* find differences between various states, i.e. index, working tree, commit-tree
* [x] API documentation
Expand Down Expand Up @@ -143,8 +143,6 @@ Check out the [performance discussion][git-traverse-performance] as well.
* [x] convert URL to string
* [x] API documentation
* [ ] Some examples
- **deviation**
* URLs may not contain passwords, which cannot be represent here and if present, will be ignored.

### git-protocol
* _abstract over protocol versions to allow delegates to deal only with a single way of doing things_
Expand Down Expand Up @@ -467,7 +465,7 @@ See its [README.md](https://github.com/Byron/gitoxide/blob/main/git-lock/README.
* [x] access to refs and objects
* **credentials**
* [x] run `git credential` directly
* [x] use credential helper configuration and to obtain credentials with `git_credential::helper::Cascade`
* [x] use credential helper configuration and to obtain credentials with `git_credentials::helper::Cascade`
* **config**
* [ ] facilities to apply the [url-match](https://git-scm.com/docs/git-config#Documentation/git-config.txt-httplturlgt) algorithm and to
[normalize urls](https://github.com/git/git/blob/be1a02a17ede4082a86dfbfee0f54f345e8b43ac/urlmatch.c#L109:L109) before comparison.
Expand Down Expand Up @@ -504,11 +502,12 @@ See its [README.md](https://github.com/Byron/gitoxide/blob/main/git-lock/README.
* **remotes**
* [ ] clone
* [ ] shallow
* [ ] fetch
* [ ] [bundles](https://git-scm.com/docs/git-bundle)
* [x] fetch
* [ ] push
* [x] ls-refs
* [ ] ls-refs with ref-spec filter
* [ ] list, find by name
* [x] ls-refs with ref-spec filter
* [x] list, find by name
* [x] create in memory
* [ ] groups
* [ ] [remote and branch files](https://github.com/git/git/blob/master/remote.c#L300)
Expand Down
2 changes: 1 addition & 1 deletion etc/check-package-size.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ echo "in root: gitoxide CLI"
(enter git-odb && indent cargo diet -n --package-size-limit 120KB)
(enter git-protocol && indent cargo diet -n --package-size-limit 55KB)
(enter git-packetline && indent cargo diet -n --package-size-limit 35KB)
(enter git-repository && indent cargo diet -n --package-size-limit 200KB)
(enter git-repository && indent cargo diet -n --package-size-limit 210KB)
(enter git-transport && indent cargo diet -n --package-size-limit 60KB)
(enter gitoxide-core && indent cargo diet -n --package-size-limit 90KB)
25 changes: 14 additions & 11 deletions git-config/src/file/access/comfort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{borrow::Cow, convert::TryFrom};

use bstr::BStr;

use crate::{file::MetadataFilter, lookup, parse::section, value, File};
use crate::{file::MetadataFilter, value, File};

/// Comfortable API for accessing values
impl<'event> File<'event> {
Expand Down Expand Up @@ -81,19 +81,22 @@ impl<'event> File<'event> {
filter: &mut MetadataFilter,
) -> Option<Result<bool, value::Error>> {
let section_name = section_name.as_ref();
let section_ids = self
.section_ids_by_name_and_subname(section_name, subsection_name)
.ok()?;
let key = key.as_ref();
match self.raw_value_filter(section_name, subsection_name, key, filter) {
Ok(v) => Some(crate::Boolean::try_from(v).map(|b| b.into())),
Err(lookup::existing::Error::KeyMissing) => {
let section = self
.section_filter(section_name, subsection_name, filter)
.ok()
.flatten()?;
let key = section::Key::try_from(key).ok()?;
section.key_and_value_range_by(&key).map(|_| Ok(true))
for section_id in section_ids.rev() {
let section = self.sections.get(&section_id).expect("known section id");
if !filter(section.meta()) {
continue;
}
match section.value_implicit(key) {
Some(Some(v)) => return Some(crate::Boolean::try_from(v).map(|b| b.into())),
Some(None) => return Some(Ok(true)),
None => continue,
}
Err(_err) => None,
}
None
}

/// Like [`value()`][File::value()], but returning an `Option` if the integer wasn't found.
Expand Down
9 changes: 6 additions & 3 deletions git-config/src/file/mutable/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ impl<'a, 'event> SectionMut<'a, 'event> {
}

body.push(Event::SectionKey(key));
if let Some(value) = value {
body.extend(self.whitespace.key_value_separators());
body.push(Event::Value(escape_value(value).into()));
match value {
Some(value) => {
body.extend(self.whitespace.key_value_separators());
body.push(Event::Value(escape_value(value).into()));
}
None => body.push(Event::Value(Cow::Borrowed("".into()))),
}
if self.implicit_newline {
body.push(Event::Newline(BString::from(self.newline.to_vec()).into()));
Expand Down
16 changes: 13 additions & 3 deletions git-config/src/file/section/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,32 @@ impl<'event> Body<'event> {
/// Note that we consider values without key separator `=` non-existing.
#[must_use]
pub fn value(&self, key: impl AsRef<str>) -> Option<Cow<'_, BStr>> {
self.value_implicit(key).flatten()
}

/// Retrieves the last matching value in a section with the given key, if present, and indicates an implicit value with `Some(None)`,
/// and a non-existing one as `None`
#[must_use]
pub fn value_implicit(&self, key: impl AsRef<str>) -> Option<Option<Cow<'_, BStr>>> {
let key = Key::from_str_unchecked(key.as_ref());
let (_key_range, range) = self.key_and_value_range_by(&key)?;
let range = range?;
let range = match range {
None => return Some(None),
Some(range) => range,
};
let mut concatenated = BString::default();

for event in &self.0[range] {
match event {
Event::Value(v) => {
return Some(normalize_bstr(v.as_ref()));
return Some(Some(normalize_bstr(v.as_ref())));
}
Event::ValueNotDone(v) => {
concatenated.push_str(v.as_ref());
}
Event::ValueDone(v) => {
concatenated.push_str(v.as_ref());
return Some(normalize_bstring(concatenated));
return Some(Some(normalize_bstring(concatenated)));
}
_ => (),
}
Expand Down
22 changes: 10 additions & 12 deletions git-config/src/file/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ impl<'event> File<'event> {
.get(&section_name)
.ok_or(lookup::existing::Error::SectionMissing)?;
let mut maybe_ids = None;
// Don't simplify if and matches here -- the for loop currently needs
// `n + 1` checks, while the if and matches will result in the for loop
// needing `2n` checks.
if let Some(subsection_name) = subsection_name {
let subsection_name: &BStr = subsection_name.into();
for node in section_ids {
Expand All @@ -152,16 +149,17 @@ impl<'event> File<'event> {
) -> Result<impl Iterator<Item = SectionId> + '_, lookup::existing::Error> {
let section_name = section::Name::from_str_unchecked(section_name);
match self.section_lookup_tree.get(&section_name) {
Some(lookup) => Ok(lookup.iter().flat_map({
let section_order = &self.section_order;
move |node| match node {
SectionBodyIdsLut::Terminal(v) => Box::new(v.iter().copied()) as Box<dyn Iterator<Item = _>>,
SectionBodyIdsLut::NonTerminal(v) => Box::new({
let v: Vec<_> = v.values().flatten().copied().collect();
section_order.iter().filter(move |a| v.contains(a)).copied()
}),
Some(lookup) => {
let mut lut = Vec::with_capacity(self.section_order.len());
for node in lookup {
match node {
SectionBodyIdsLut::Terminal(v) => lut.extend(v.iter().copied()),
SectionBodyIdsLut::NonTerminal(v) => lut.extend(v.values().flatten().copied()),
}
}
})),

Ok(self.section_order.iter().filter(move |a| lut.contains(a)).copied())
}
None => Err(lookup::existing::Error::SectionMissing),
}
}
Expand Down
2 changes: 1 addition & 1 deletion git-config/src/parse/nom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ fn config_value<'a>(i: &'a [u8], dispatch: &mut impl FnMut(Event<'a>)) -> IResul
} else {
// This is a special way of denoting 'empty' values which a lot of code depends on.
// Hence, rather to fix this everywhere else, leave it here and fix it where it matters, namely
// when it's about differentiating between a missing key-vaue separator, and one followed by emptiness.
// when it's about differentiating between a missing key-value separator, and one followed by emptiness.
dispatch(Event::Value(Cow::Borrowed("".into())));
Ok((i, 0))
}
Expand Down
58 changes: 50 additions & 8 deletions git-config/tests/file/access/read_only.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,21 @@ fn get_value_for_all_provided_values() -> crate::Result {

assert!(
config.value::<Boolean>("core", None, "bool-implicit").is_err(),
"this cannot work like in git as the value isn't there for us"
"this cannot work like in git as the original value isn't there for us"
);
assert!(
config.boolean("core", None, "bool-implicit").expect("present")?,
"this should work"
"implicit booleans resolve to being true"
);
assert_eq!(
config.string("core", None, "bool-implicit"),
None,
"unset values are not present"
);
assert_eq!(
config.strings("core", None, "bool-implicit").expect("present"),
&[cow_str("")],
"unset values show up as empty within a string array"
);

assert_eq!(config.string("doesnt", None, "exist"), None);
Expand Down Expand Up @@ -146,8 +156,6 @@ fn get_value_for_all_provided_values() -> crate::Result {
Ok(())
}

/// There was a regression where lookup would fail because we only checked the
/// last section entry for any given section and subsection
#[test]
fn get_value_looks_up_all_sections_before_failing() -> crate::Result {
let config = r#"
Expand All @@ -163,14 +171,17 @@ fn get_value_looks_up_all_sections_before_failing() -> crate::Result {
// Checks that we check the last entry first still
assert!(
!file.value::<Boolean>("core", None, "bool-implicit")?.0,
"this one can't do it, needs special handling"
"implicit bool is invisible to `value` and boolean is the only value we want. Would have to special case it."
);
assert!(
!file.boolean("core", None, "bool-implicit").expect("present")?,
"this should work, but doesn't yet"
file.boolean("core", None, "bool-implicit").expect("present")?,
"correct handling of booleans is implemented specifically"
);

assert!(!file.value::<Boolean>("core", None, "bool-explicit")?.0);
assert!(
!file.value::<Boolean>("core", None, "bool-explicit")?.0,
"explicit values always work"
);

Ok(())
}
Expand Down Expand Up @@ -313,3 +324,34 @@ fn multi_line_value_outer_quotes_escaped_inner_quotes() {
let expected = r#"!f() { git status; git add -A; git commit -m "$1"; git push -f; git log -1; }; f; unset f"#;
assert_eq!(config.raw_value("alias", None, "save").unwrap().as_ref(), expected);
}

#[test]
fn overrides_with_implicit_booleans_work_in_single_section() {
let config = r#"
[a]
b = false
b
"#;
let config = File::try_from(config).unwrap();
assert_eq!(
config.boolean("a", None, "b"),
Some(Ok(true)),
"empty implicit booleans "
);
}

#[test]
fn overrides_with_implicit_booleans_work_across_sections() {
let config = r#"
[a]
b = false
[a]
b
"#;
let config = File::try_from(config).unwrap();
assert_eq!(
config.boolean("a", None, "b"),
Some(Ok(true)),
"empty implicit booleans "
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
use bstr::{BString, ByteSlice};
use git_config::file::init::{self};

use crate::file::init::from_paths::includes::conditional::git_init;
use crate::file::{
cow_str,
init::from_paths::{escape_backslashes, includes::conditional::options_with_git_dir},
Expand Down Expand Up @@ -216,7 +217,7 @@ fn write_main_config(
env: &GitEnv,
overwrite_config_location: ConfigLocation,
) -> crate::Result {
git_repository::init(env.worktree_dir())?;
git_init(env.worktree_dir(), false)?;

if overwrite_config_location == ConfigLocation::Repo {
write_append_config_value(env.git_dir().join("config"), "base-value")?;
Expand Down
12 changes: 12 additions & 0 deletions git-config/tests/file/init/from_paths/includes/conditional/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use git_config::{
file::{includes, init},
path, File,
};
use git_repository as git;
use tempfile::tempdir;

use crate::file::{cow_str, init::from_paths::escape_backslashes};
Expand Down Expand Up @@ -138,6 +139,17 @@ fn options_with_git_dir(git_dir: &Path) -> init::Options<'_> {
}
}

fn git_init(path: impl AsRef<std::path::Path>, bare: bool) -> crate::Result<git::Repository> {
Ok(git::ThreadSafeRepository::init_opts(
path,
bare.then(|| git::create::Kind::Bare)
.unwrap_or(git::create::Kind::WithWorktree),
git::create::Options::default(),
git::open::Options::isolated(),
)?
.to_thread_local())
}

fn create_symlink(from: impl AsRef<Path>, to: impl AsRef<Path>) {
std::fs::create_dir_all(from.as_ref().parent().unwrap()).unwrap();
#[cfg(not(windows))]
Expand Down
Loading

0 comments on commit def53b3

Please sign in to comment.