From 68735cbf0fdd2a603faad30fa490c2483e10c8e6 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 28 Mar 2016 21:07:39 -0700 Subject: [PATCH 1/2] libgit2-sys: Add git_repository_open_ext and its flags --- libgit2-sys/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libgit2-sys/lib.rs b/libgit2-sys/lib.rs index 86a21d9b91..64e5f2a5eb 100644 --- a/libgit2-sys/lib.rs +++ b/libgit2-sys/lib.rs @@ -820,6 +820,14 @@ git_enum! { } } +git_enum! { + pub enum git_repository_open_flag_t { + GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0), + GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1), + GIT_REPOSITORY_OPEN_BARE = (1 << 2), + } +} + #[repr(C)] pub struct git_repository_init_options { pub version: c_uint, @@ -1382,6 +1390,10 @@ extern { pub fn git_repository_free(repo: *mut git_repository); pub fn git_repository_open(repo: *mut *mut git_repository, path: *const c_char) -> c_int; + pub fn git_repository_open_ext(repo: *mut *mut git_repository, + path: *const c_char, + flags: c_uint, + ceiling_dirs: *const c_char) -> c_int; pub fn git_repository_init(repo: *mut *mut git_repository, path: *const c_char, is_bare: c_uint) -> c_int; From 5589a1a0653abf50fb7e90dce89327e9bceb94c9 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 28 Mar 2016 23:02:41 -0700 Subject: [PATCH 2/2] Add git::Repository::open_ext and RepositoryOpenFlags, with tests --- src/error.rs | 7 ++++++ src/lib.rs | 14 +++++++++++ src/repo.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index a8f69b0e63..ab4173a075 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +use std::env::JoinPathsError; use std::ffi::{CStr, NulError}; use std::error; use std::fmt; @@ -214,6 +215,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: JoinPathsError) -> Error { + Error::from_str(error::Error::description(&e)) + } +} + #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 122cdd2569..8a224e2247 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -410,6 +410,20 @@ Flags for APIs that add files matching pathspec } } +bitflags! { + #[doc = " +Flags for `Repository::open_ext` +"] + flags RepositoryOpenFlags: u32 { + /// Only open the specified path; don't walk upward searching. + const REPOSITORY_OPEN_NO_SEARCH = raw::GIT_REPOSITORY_OPEN_NO_SEARCH as u32, + /// Search across filesystem boundaries. + const REPOSITORY_OPEN_CROSS_FS = raw::GIT_REPOSITORY_OPEN_CROSS_FS as u32, + /// Force opening as bare repository, and defer loading its config. + const REPOSITORY_OPEN_BARE = raw::GIT_REPOSITORY_OPEN_BARE as u32, + } +} + bitflags! { #[doc = " Flags for the return value of `Repository::revparse` diff --git a/src/repo.rs b/src/repo.rs index 2d1da2bee5..d2901ed69d 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -1,11 +1,12 @@ -use std::ffi::{CStr, CString}; +use std::env; +use std::ffi::{CStr, CString, OsStr}; use std::iter::IntoIterator; use std::mem; use std::path::Path; use std::str; use libc::{c_int, c_char, size_t, c_void, c_uint}; -use {raw, Revspec, Error, init, Object, RepositoryState, Remote, Buf}; +use {raw, Revspec, Error, init, Object, RepositoryOpenFlags, RepositoryState, Remote, Buf}; use {ResetType, Signature, Reference, References, Submodule, Blame, BlameOptions}; use {Branches, BranchType, Index, Config, Oid, Blob, Branch, Commit, Tree}; use {AnnotatedCommit, MergeOptions, SubmoduleIgnore, SubmoduleStatus}; @@ -59,6 +60,43 @@ impl Repository { } } + /// Find and open an existing repository, with additional options. + /// + /// If flags contains REPOSITORY_OPEN_NO_SEARCH, the path must point + /// directly to a repository; otherwise, this may point to a subdirectory + /// of a repository, and `open_ext` will search up through parent + /// directories. + /// + /// If flags contains REPOSITORY_OPEN_CROSS_FS, the search through parent + /// directories will not cross a filesystem boundary (detected when the + /// stat st_dev field changes). + /// + /// If flags contains REPOSITORY_OPEN_BARE, force opening the repository as + /// bare even if it isn't, ignoring any working directory, and defer + /// loading the repository configuration for performance. + /// + /// ceiling_dirs specifies a list of paths that the search through parent + /// directories will stop before entering. Use the functions in std::env + /// to construct or manipulate such a path list. + pub fn open_ext(path: P, + flags: RepositoryOpenFlags, + ceiling_dirs: I) + -> Result + where P: AsRef, O: AsRef, I: IntoIterator { + init(); + let path = try!(path.as_ref().into_c_string()); + let ceiling_dirs_os = try!(env::join_paths(ceiling_dirs)); + let ceiling_dirs = try!(ceiling_dirs_os.into_c_string()); + let mut ret = 0 as *mut raw::git_repository; + unsafe { + try_call!(raw::git_repository_open_ext(&mut ret, + path, + flags.bits() as c_uint, + ceiling_dirs)); + Ok(Binding::from_raw(ret)) + } + } + /// Attempt to open an already-existing repository at or above `path` /// /// This starts at `path` and looks up the filesystem hierarchy @@ -1663,6 +1701,7 @@ impl RepositoryInitOptions { #[cfg(test)] mod tests { + use std::ffi::OsStr; use std::fs; use std::path::Path; use tempdir::TempDir; @@ -1754,6 +1793,30 @@ mod tests { ::test::realpath(&td.path().join("")).unwrap()); } + #[test] + fn smoke_open_ext() { + let td = TempDir::new("test").unwrap(); + let subdir = td.path().join("subdir"); + fs::create_dir(&subdir).unwrap(); + Repository::init(td.path()).unwrap(); + + let repo = Repository::open_ext(&subdir, ::RepositoryOpenFlags::empty(), &[] as &[&OsStr]).unwrap(); + assert!(!repo.is_bare()); + assert_eq!(::test::realpath(&repo.path()).unwrap(), + ::test::realpath(&td.path().join(".git")).unwrap()); + + let repo = Repository::open_ext(&subdir, ::REPOSITORY_OPEN_BARE, &[] as &[&OsStr]).unwrap(); + assert!(repo.is_bare()); + assert_eq!(::test::realpath(&repo.path()).unwrap(), + ::test::realpath(&td.path().join(".git")).unwrap()); + + let err = Repository::open_ext(&subdir, ::REPOSITORY_OPEN_NO_SEARCH, &[] as &[&OsStr]).err().unwrap(); + assert_eq!(err.code(), ::ErrorCode::NotFound); + + let err = Repository::open_ext(&subdir, ::RepositoryOpenFlags::empty(), &[&subdir]).err().unwrap(); + assert_eq!(err.code(), ::ErrorCode::NotFound); + } + fn graph_repo_init() -> (TempDir, Repository) { let (_td, repo) = ::test::repo_init(); {