fix: handle symlinks correctly when adding and flushing files in the repository

This commit is contained in:
Nikita Galaiko 2023-11-02 08:43:25 +01:00 committed by GitButler
parent 2f4e26475e
commit 0edbfc1a54
3 changed files with 111 additions and 8 deletions

View File

@ -802,16 +802,24 @@ fn add_wd_path(
) -> Result<()> {
let file_path = dir.join(rel_file_path);
let metadata = file_path
.metadata()
.with_context(|| "failed to get metadata for".to_string())?;
let metadata = std::fs::symlink_metadata(&file_path).context("failed to get metadata for")?;
let modify_time = FileTime::from_last_modification_time(&metadata);
let create_time = FileTime::from_creation_time(&metadata).unwrap_or(modify_time);
// look for files that are bigger than 4GB, which are not supported by git
// insert a pointer as the blob content instead
// TODO: size limit should be configurable
let blob = if metadata.len() > 100_000_000 {
let blob = if metadata.is_symlink() {
// it's a symlink, make the content the path of the link
let link_target = std::fs::read_link(&file_path)?;
// if the link target is inside the project repository, make it relative
let link_target = link_target.strip_prefix(dir).unwrap_or(&link_target);
// bytes dance
let path_str = link_target.to_str().unwrap();
let bytes: &[u8] = path_str.as_bytes();
gb_repository.git_repository.blob(bytes)?
} else if metadata.len() > 100_000_000 {
tracing::warn!(
project_id = %gb_repository.project.id,
path = %file_path.display(),
@ -837,10 +845,7 @@ fn add_wd_path(
let lfs_path = lfs_objects_dir.join(sha);
std::fs::copy(file_path, lfs_path)?;
gb_repository
.git_repository
.blob(lfs_pointer.as_bytes())
.unwrap()
gb_repository.git_repository.blob(lfs_pointer.as_bytes())?
} else {
// read the file into a blob, get the object id
gb_repository.git_repository.blob_path(&file_path)?

View File

@ -0,0 +1,97 @@
use gblib::{gb_repository, project_repository, projects};
use crate::{common::TestProject, paths};
mod init {
use super::*;
#[test]
fn handle_file_symlink() {
let test_project = TestProject::default();
let data_dir = paths::data_dir();
let projects = projects::Controller::from(&data_dir);
let project = projects
.add(test_project.path())
.expect("failed to add project");
std::fs::write(project.path.join("file"), "content").unwrap();
std::fs::hard_link(project.path.join("file"), project.path.join("link")).unwrap();
let project_repository = project_repository::Repository::try_from(project).unwrap();
gb_repository::Repository::open(&data_dir, &project_repository, None).unwrap();
}
#[test]
fn handle_dir_symlink() {
let test_project = TestProject::default();
let data_dir = paths::data_dir();
let projects = projects::Controller::from(&data_dir);
let project = projects
.add(test_project.path())
.expect("failed to add project");
std::fs::create_dir(project.path.join("dir")).unwrap();
std::fs::write(project.path.join("dir/file"), "content").unwrap();
std::os::unix::fs::symlink(project.path.join("dir"), project.path.join("dir_link"))
.unwrap();
let project_repository = project_repository::Repository::try_from(project).unwrap();
gb_repository::Repository::open(&data_dir, &project_repository, None).unwrap();
}
}
mod flush {
use super::*;
#[test]
fn handle_file_symlink() {
let test_project = TestProject::default();
let data_dir = paths::data_dir();
let projects = projects::Controller::from(&data_dir);
let project = projects
.add(test_project.path())
.expect("failed to add project");
let project_repository = project_repository::Repository::try_from(&project).unwrap();
let gb_repo =
gb_repository::Repository::open(&data_dir, &project_repository, None).unwrap();
std::fs::write(project.path.join("file"), "content").unwrap();
std::fs::hard_link(project.path.join("file"), project.path.join("link")).unwrap();
gb_repo.flush(&project_repository, None).unwrap();
}
#[test]
fn handle_dir_symlink() {
let test_project = TestProject::default();
let data_dir = paths::data_dir();
let projects = projects::Controller::from(&data_dir);
let project = projects
.add(test_project.path())
.expect("failed to add project");
let project_repository = project_repository::Repository::try_from(&project).unwrap();
let gb_repo =
gb_repository::Repository::open(&data_dir, &project_repository, None).unwrap();
std::fs::create_dir(project.path.join("dir")).unwrap();
std::fs::write(project.path.join("dir/file"), "content").unwrap();
std::os::unix::fs::symlink(project.path.join("dir"), project.path.join("dir_link"))
.unwrap();
gb_repo.flush(&project_repository, None).unwrap();
}
}

View File

@ -1,6 +1,7 @@
#![allow(clippy::dbg_macro)]
mod common;
mod gb_repository;
mod paths;
mod projects;
mod virtual_branches;