summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Gerace <nickagerace@gmail.com>2022-12-20 14:15:41 -0500
committerNick Gerace <nickagerace@gmail.com>2022-12-20 14:27:32 -0500
commit0dc427908638d1494a47d60e678d4b934c6355a9 (patch)
tree3f6b02d98fa4359cd9fd578784146bc060acc232
parentfa71d230e8f6efe28a1c9c11874432ca575e3440 (diff)
downloadgfold-0dc427908638d1494a47d60e678d4b934c6355a9.zip
Assume unpushed if unable to resolve ref from short name
- When checking if unpushed and attempting to resolve the reference from a short name, ignore the error and assume we need to push - For now, the error is logged to stderr at the debug level, but we may need to perform more advanced error handling in the future - Use tempfile instead of creating directories under "target" - The root temporary directory and its descendants will be immediately deleted as soon as the value for root is dropped from scope - This is far less error prone than the previous method - Remove bors to increase developer and release feedback loop Signed-off-by: Nick Gerace <nickagerace@gmail.com>
-rw-r--r--.github/workflows/ci.yml5
-rw-r--r--CHANGELOG.md9
-rw-r--r--Cargo.lock52
-rw-r--r--README.md5
-rw-r--r--bors.toml8
-rw-r--r--crates/bench-loosely/Cargo.toml2
-rw-r--r--crates/gfold/Cargo.toml1
-rw-r--r--crates/gfold/src/cli.rs2
-rw-r--r--crates/gfold/src/config.rs4
-rw-r--r--crates/gfold/src/main.rs278
-rw-r--r--crates/gfold/src/report.rs14
-rw-r--r--crates/size/Cargo.toml2
12 files changed, 209 insertions, 173 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e61c967..cde6f1d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,11 +2,10 @@ name: ci
on:
push:
branches:
- - staging
- - trying
+ - "main"
pull_request:
branches:
- - main
+ - "main"
paths:
- "**.rs"
- "Cargo.*"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52aedff..ba217e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,14 +8,19 @@ For new changes prior to version 4.0.0, please see [CHANGELOG_PRE_V4](./docs/CHA
## Unreleased
-The latest version contains all changes.
+<!-- The latest version contains all changes. -->
+
+### Changed
+
+- Bump dependencies
+- When checking if unpushed and attempting to resolve the reference from a short name, ignore the error and assume we need to push
## 4.1.1 - 2022-12-19
### Changed
+- Bump dependencies
- Ensure dependencies have their minor version fields locked
-- Update dependencies
### Notes
diff --git a/Cargo.lock b/Cargo.lock
index 234cfab..5f1dd3f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -241,6 +241,15 @@ dependencies = [
]
[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -274,6 +283,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
+ "tempfile",
"termcolor",
"thiserror",
"toml",
@@ -339,6 +349,15 @@ dependencies = [
]
[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -350,9 +369,9 @@ dependencies = [
[[package]]
name = "is-terminal"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
+checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
@@ -446,11 +465,11 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi 0.2.6",
"libc",
]
@@ -593,6 +612,15 @@ dependencies = [
]
[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -680,6 +708,20 @@ dependencies = [
]
[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/README.md b/README.md
index f6aa96e..90bd90c 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,7 @@
[![latest release tag](https://img.shields.io/github/v/tag/nickgerace/gfold?sort=semver&logo=git&logoColor=white&label=version&style=flat-square&color=blue)](https://github.com/nickgerace/gfold/releases/latest)
[![crates.io version](https://img.shields.io/crates/v/gfold?style=flat-square&logo=rust&color=orange)](https://crates.io/crates/gfold)
[![license](https://img.shields.io/github/license/nickgerace/gfold?style=flat-square&logo=apache&color=silver)](./LICENSE)
-[![build status](https://img.shields.io/github/workflow/status/nickgerace/gfold/merge/main?style=flat-square&logo=github&logoColor=white)](https://github.com/nickgerace/gfold/actions?query=workflow%3Amerge+branch%3Amain)
-[![bors enabled](https://bors.tech/images/badge_small.svg)](https://app.bors.tech/repositories/42509)
+[![build status](https://img.shields.io/github/actions/workflow/status/nickgerace/gfold/workflows/ci.yml?branch=main&style=flat-square&logo=github&logoColor=white)](https://img.shields.io/github/actions/workflow/status/nickgerace/gfold/workflows/ci.yml?branch=main&style=flat-square&logo=github&logoColor=white)
`gfold` is a CLI-driven application that helps you keep track of multiple Git repositories.
@@ -220,4 +219,4 @@ Name | Type | Description
[Arch Linux community repository](https://archlinux.org/packages/community/x86_64/gfold/) | packaging | the `gfold` package _(note: before moving to the community repository, the [AUR](https://github.com/orhun/PKGBUILDs) was previously used for distribution)_
["One Hundred Rust Binaries"](https://www.wezm.net/v2/posts/2020/100-rust-binaries/page2/) | article | featured `gfold`
[nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/version-management/git-and-tools/gfold/default.nix) | packaging | the `gfold` package
-[nvim-gfold.lua](https://github.com/AckslD/nvim-gfold.lua) | project | a `neovim` plugin for `gfold` *([announcement Reddit post](https://www.reddit.com/r/neovim/comments/t209wy/introducing_nvimgfoldlua/))* \ No newline at end of file
+[nvim-gfold.lua](https://github.com/AckslD/nvim-gfold.lua) | project | a `neovim` plugin for `gfold` *([announcement Reddit post](https://www.reddit.com/r/neovim/comments/t209wy/introducing_nvimgfoldlua/))*
diff --git a/bors.toml b/bors.toml
deleted file mode 100644
index 83f1433..0000000
--- a/bors.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-status = [
- "Lint / Test / Build (ubuntu-latest)",
- "Test / Build (windows-latest)",
- "Test / Build (macos-latest)"
-]
-timeout_sec = 900
-delete_merged_branches = true
-update_base_for_deletes = true
diff --git a/crates/bench-loosely/Cargo.toml b/crates/bench-loosely/Cargo.toml
index b533aa1..d090406 100644
--- a/crates/bench-loosely/Cargo.toml
+++ b/crates/bench-loosely/Cargo.toml
@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-dirs = "4"
+dirs = "4.0"
diff --git a/crates/gfold/Cargo.toml b/crates/gfold/Cargo.toml
index 7802f81..934674e 100644
--- a/crates/gfold/Cargo.toml
+++ b/crates/gfold/Cargo.toml
@@ -31,3 +31,4 @@ env_logger = { version = "0.9", features = ["atty", "humantime"], default_featur
[dev-dependencies]
pretty_assertions = "1.3"
+tempfile = "3.3"
diff --git a/crates/gfold/src/cli.rs b/crates/gfold/src/cli.rs
index 4ccf474..8408bfd 100644
--- a/crates/gfold/src/cli.rs
+++ b/crates/gfold/src/cli.rs
@@ -65,7 +65,7 @@ pub fn parse_and_run() -> anyhow::Result<()> {
debug!("collected args");
let mut config = match cli.ignore_config_file {
- true => Config::new()?,
+ true => Config::try_config_default()?,
false => Config::try_config()?,
};
debug!("loaded initial config");
diff --git a/crates/gfold/src/config.rs b/crates/gfold/src/config.rs
index bb4ac57..7562cb4 100644
--- a/crates/gfold/src/config.rs
+++ b/crates/gfold/src/config.rs
@@ -86,8 +86,8 @@ impl Config {
}
/// This method does not look for the config file and uses [`EntryConfig`]'s defaults instead.
- /// It is best for testing use and when the user wishes to skip config file lookup.
- pub fn new() -> io::Result<Self> {
+ /// Use this method when the user wishes to skip config file lookup.
+ pub fn try_config_default() -> io::Result<Self> {
Self::from_entry_config(&EntryConfig::default())
}
diff --git a/crates/gfold/src/main.rs b/crates/gfold/src/main.rs
index eec0790..6b7bdb6 100644
--- a/crates/gfold/src/main.rs
+++ b/crates/gfold/src/main.rs
@@ -37,142 +37,148 @@ mod tests {
use crate::status::Status;
use anyhow::{anyhow, Result};
+
use git2::ErrorCode;
+ use git2::Oid;
use git2::Repository;
use git2::Signature;
- use git2::Oid;
+
use pretty_assertions::assert_eq;
use std::collections::BTreeMap;
+ use std::fs::File;
use std::path::{Path, PathBuf};
- use std::{env, fs, io};
+ use std::{fs, io};
+ use tempfile::tempdir;
- /// This integration test for `gfold` covers an end-to-end usage scenario. It does not
- /// _remove_ anything in the filesystem (for saftey), so you must delete the `test`
- /// directory underneath `target` to regenerate a clean dataset.
+ /// This integration test for `gfold` covers an end-to-end usage scenario. It uses the
+ /// [`tempfile`](tempfile) crate to create some repositories with varying states and levels
+ /// of nesting.
#[test]
fn integration() -> Result<()> {
- // Test directory structure within "target":
- // └── test
- // ├── bar
- // ├── baz
- // ├── foo
- // │ └── newfile
+ env_logger::builder()
+ .is_test(true)
+ .filter_level(LevelFilter::Info)
+ .try_init()?;
+
+ // Temporary directory structure:
+ // └── root
+ // ├── one (repo)
+ // │ └── file
+ // ├── two (repo)
+ // ├── three (repo)
// └── nested
- // ├── one
- // │ └── newfile
- // ├── three
- // └── two
- let test_directory = integration_init()?;
- create_directory(&test_directory)?;
-
- for name in ["foo", "bar", "baz"] {
- let current = test_directory.join(name);
- create_directory(&current)?;
- Repository::init(&current)?;
-
- if name == "foo" {
- create_file(&current.join("newfile"))?;
- }
- }
+ // ├── four (repo)
+ // ├── five (repo)
+ // │ └── file
+ // ├── six (repo)
+ // └── seven (repo)
+ let root = tempdir()?;
+ let repo_one = create_directory(&root, "one")?;
+ let repo_two = create_directory(&root, "two")?;
+ let repo_three = create_directory(&root, "three")?;
- let nested = test_directory.join("nested");
- create_directory(&nested)?;
- for name in ["one", "two", "three"] {
- let current = nested.join(name);
- create_directory(&current)?;
- let repository = Repository::init(&current)?;
+ let nested = create_directory(&root, "nested")?;
+ let repo_four = create_directory(&nested, "four")?;
+ let repo_five = create_directory(&nested, "five")?;
+ let repo_six = create_directory(&nested, "six")?;
+ let repo_seven = create_directory(&nested, "seven")?;
- if name == "one" {
- create_file(&current.join("newfile"))?;
- }
+ // Repo One
+ Repository::init(&repo_one)?;
+ create_file(&repo_one)?;
+
+ // Repo Two
+ Repository::init(&repo_two)?;
+
+ // Repo Three
+ Repository::init(&repo_three)?;
- if name == "two" {
- if let Err(e) = repository.remote("origin", "https://github.com/nickgerace/gfold") {
- if e.code() != ErrorCode::Exists {
- return Err(e.into());
- }
- }
+ // Repo Four
+ let repository = Repository::init(&repo_four)?;
+ if let Err(e) = repository.remote("origin", "https://github.com/nickgerace/gfold") {
+ if e.code() != ErrorCode::Exists {
+ return Err(e.into());
}
+ }
- if name == "three" {
- if let Err(e) = repository.remote("fork", "https://github.com/nickgerace/gfold") {
- if e.code() != ErrorCode::Exists {
- return Err(e.into());
- }
- }
+ // Repo Five
+ Repository::init(&repo_five)?;
+ create_file(&repo_five)?;
- create_branch(&repository, "feat")?;
+ // Repo Six
+ let repository = Repository::init(&repo_six)?;
+ if let Err(e) = repository.remote("fork", "https://github.com/nickgerace/gfold") {
+ if e.code() != ErrorCode::Exists {
+ return Err(e.into());
}
}
+ commit_head_and_create_branch(&repository, "feat")?;
- let mut config = Config::new()?;
- config.path = test_directory.clone();
+ // Repo Seven
+ let repository = Repository::init(&repo_seven)?;
+ if let Err(e) = repository.remote("origin", "https://github.com/nickgerace/gfold") {
+ if e.code() != ErrorCode::Exists {
+ return Err(e.into());
+ }
+ }
+ commit_head_and_create_branch(&repository, "needtopush")?;
+ repository.set_head("refs/heads/needtopush")?;
+
+ // Run once with default display mode.
+ let mut config = Config::try_config_default()?;
+ config.path = root.path().to_path_buf();
config.color_mode = ColorMode::Never;
- assert!(run::run(&config).is_ok());
+ run::run(&config)?;
// Now, let's ensure our reports are what we expect.
let mut expected_reports: LabeledReports = BTreeMap::new();
-
- let key = test_directory
+ let expected_reports_key = root
+ .path()
.to_str()
.ok_or_else(|| anyhow!("could not convert PathBuf to &str"))?
.to_string();
- let mut reports = vec![
- Report::new(
- &test_directory.join("foo"),
- "HEAD",
- &Status::Unclean,
- None,
- None,
- )?,
- Report::new(
- &test_directory.join("bar"),
- "HEAD",
- &Status::Clean,
- None,
- None,
- )?,
- Report::new(
- &test_directory.join("baz"),
- "HEAD",
- &Status::Clean,
- None,
- None,
- )?,
+ let mut expected_reports_raw = vec![
+ Report::new(&repo_one, "HEAD", &Status::Unclean, None, None)?,
+ Report::new(&repo_two, "HEAD", &Status::Clean, None, None)?,
+ Report::new(&repo_three, "HEAD", &Status::Clean, None, None)?,
];
- reports.sort_by(|a, b| a.name.cmp(&b.name));
- expected_reports.insert(Some(key), reports);
+ expected_reports_raw.sort_by(|a, b| a.name.cmp(&b.name));
+ expected_reports.insert(Some(expected_reports_key), expected_reports_raw);
- let nested_test_dir = test_directory.join("nested");
- let key = nested_test_dir
+ // Add nested reports to the expected reports map.
+ let nested_expected_reports_key = nested
.to_str()
.ok_or_else(|| anyhow!("could not convert PathBuf to &str"))?
.to_string();
- let mut reports = vec![
+ let mut nested_expected_reports_raw = vec![
Report::new(
- &nested_test_dir.join("one"),
+ &repo_four,
"HEAD",
- &Status::Unclean,
- None,
+ &Status::Clean,
+ Some("https://github.com/nickgerace/gfold".to_string()),
None,
)?,
+ Report::new(&repo_five, "HEAD", &Status::Unclean, None, None)?,
Report::new(
- &nested_test_dir.join("two"),
- "HEAD",
- &Status::Clean,
+ &repo_six,
+ "master",
+ &Status::Unpushed,
Some("https://github.com/nickgerace/gfold".to_string()),
None,
)?,
Report::new(
- &nested_test_dir.join("three"),
- "HEAD",
- &Status::Clean,
+ &repo_seven,
+ "needtopush",
+ &Status::Unpushed,
Some("https://github.com/nickgerace/gfold".to_string()),
None,
)?,
];
- reports.sort_by(|a, b| a.name.cmp(&b.name));
- expected_reports.insert(Some(key), reports);
+ nested_expected_reports_raw.sort_by(|a, b| a.name.cmp(&b.name));
+ expected_reports.insert(
+ Some(nested_expected_reports_key),
+ nested_expected_reports_raw,
+ );
// Use classic display mode to avoid collecting email results.
config.display_mode = DisplayMode::Classic;
@@ -184,87 +190,75 @@ mod tests {
found_labeled_reports_sorted.insert(labeled_report.0.clone(), value.clone());
}
- assert_eq!(found_labeled_reports_sorted, expected_reports);
+ assert_eq!(
+ expected_reports, // expected
+ found_labeled_reports_sorted // actual
+ );
Ok(())
}
- /// Ensure we are underneath the repository root. Safely create the test directory.
- fn integration_init() -> Result<PathBuf> {
- let manifest_directory = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
- let repo_root = manifest_directory
- .parent()
- .ok_or_else(|| anyhow!("could not get parent"))?
- .parent()
- .ok_or_else(|| anyhow!("could not get parent"))?;
- assert!(Repository::open(repo_root).is_ok());
-
- let target = repo_root.join("target");
- create_directory(&target)?;
- let test = target.join("test");
- Ok(test)
- }
+ fn create_directory<P: AsRef<Path>>(parent: P, name: &str) -> Result<PathBuf> {
+ let parent = parent.as_ref();
+ let new_directory = parent.join(name);
- fn create_directory(path: &Path) -> Result<()> {
- if let Err(e) = fs::create_dir(path) {
+ if let Err(e) = fs::create_dir(&new_directory) {
if e.kind() != io::ErrorKind::AlreadyExists {
return Err(anyhow!(
"could not create directory ({:?}) due to error kind: {:?}",
- path,
+ &new_directory,
e.kind()
));
}
}
- Ok(())
+ Ok(new_directory)
}
- fn create_file(path: &Path) -> Result<()> {
- if let Err(e) = fs::File::create(path) {
- if e.kind() != io::ErrorKind::AlreadyExists {
- return Err(anyhow!(
- "could not create file ({:?}) due to error kind: {:?}",
- path,
- e.kind()
- ));
- }
- }
+ fn create_file<P: AsRef<Path>>(parent: P) -> Result<()> {
+ let parent = parent.as_ref();
+ File::create(parent.join("file"))?;
Ok(())
}
- fn create_branch(repository: &Repository, name: &str) -> Result<()> {
- // we need to commit something before branching
- let commit_oid = commit(repository)?;
- repository.branch(name, &repository.find_commit(commit_oid).unwrap(), true).unwrap();
-
+ fn commit_head_and_create_branch(repository: &Repository, name: &str) -> Result<()> {
+ // We need to commit at least once before branching.
+ let commit_oid = commit(repository, "HEAD")?;
+ let commit = repository.find_commit(commit_oid)?;
+ repository.branch(name, &commit, true)?;
Ok(())
}
- // taken from https://github.com/rust-lang/git2-rs/pull/885
- fn commit(repository: &Repository) -> Result<Oid> {
- // We will commit the content of the index
+ // Source: https://github.com/rust-lang/git2-rs/pull/885
+ fn commit(repository: &Repository, update_ref: &str) -> Result<Oid> {
+ // We will commit the contents of the index.
let mut index = repository.index()?;
let tree_oid = index.write_tree()?;
let tree = repository.find_tree(tree_oid)?;
- let parent_commit = match repository.revparse_single("HEAD") {
- Ok(obj) => Some(obj.into_commit().unwrap()),
- // First commit so no parent commit
+ // If this is the first commit, there is no parent. If the object returned by
+ // "revparse_single" cannot be converted into a commit, then it isn't a commit and we know
+ // there is no parent _commit_.
+ let maybe_parent = match repository.revparse_single("HEAD") {
+ Ok(object) => match object.into_commit() {
+ Ok(commit) => Some(commit),
+ Err(_) => None,
+ },
Err(e) if e.code() == ErrorCode::NotFound => None,
- Err(_e) => panic!(),
+ Err(e) => return Err(e.into()),
};
let mut parents = Vec::new();
- if parent_commit.is_some() {
- parents.push(parent_commit.as_ref().unwrap());
- }
+ if let Some(parent) = maybe_parent.as_ref() {
+ parents.push(parent);
+ };
- let sig = Signature::now("Bob", "bob@bob").unwrap();
+ let signature = Signature::now("Bob", "bob@bob")?;
Ok(repository.commit(
- Some("HEAD"),
- &sig,
- &sig,
+ Some(update_ref),
+ &signature,
+ &signature,
"hello",
&tree,
- &parents[..],
+ parents.as_ref(),
)?)
}
}
diff --git a/crates/gfold/src/report.rs b/crates/gfold/src/report.rs
index 4d5c20c..6043235 100644
--- a/crates/gfold/src/report.rs
+++ b/crates/gfold/src/report.rs
@@ -103,7 +103,7 @@ pub fn generate_reports(path: &Path, display_mode: &DisplayMode) -> anyhow::Resu
/// Generates a report with a given path.
fn generate_report(repo_path: &Path, include_email: bool) -> anyhow::Result<Report> {
debug!(
- "attemping to generate report for repository at path: {:?}",
+ "attempting to generate report for repository at path: {:?}",
repo_path
);
let repo = Repository::open(repo_path)?;
@@ -179,14 +179,18 @@ fn is_unpushed(
match head.shorthand() {
Some(v) => v,
None => {
- trace!("assuming unpushed; could not determine shorthand for head");
+ debug!("assuming unpushed; could not determine shorthand for head");
return Ok(true);
}
}
);
- let remote_head = repo
- .resolve_reference_from_short_name(&remote)?
- .peel_to_commit()?;
+ let remote_head = match repo.resolve_reference_from_short_name(&remote) {
+ Ok(reference) => reference.peel_to_commit()?,
+ Err(e) => {
+ debug!("assuming unpushed; could not resolve remote reference from short name (ignored error: {})", e);
+ return Ok(true);
+ }
+ };
Ok(
matches!(repo.graph_ahead_behind(local_head.id(), remote_head.id()), Ok(number_unique_commits) if number_unique_commits.0 > 0),
)
diff --git a/crates/size/Cargo.toml b/crates/size/Cargo.toml
index 1a6bc06..9b56e93 100644
--- a/crates/size/Cargo.toml
+++ b/crates/size/Cargo.toml
@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-dirs = "4"
+dirs = "4.0"