diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ab0961f1f..c842f1eec5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,7 @@ The following steps can be used to update libgit2: can be helpful for seeing what has changed. The project has recently started labeling API and ABI breaking changes with labels: + Alternatively, running `git diff [PREV_VERSION]..[NEW_VERSION] --ignore-all-space -- include/` can provide an overview of changes made to the API. 4. Once you have everything functional, publish a PR with the updates. ## Release process diff --git a/Cargo.toml b/Cargo.toml index 6359ac058c..987479b633 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ url = "2.0" bitflags = "2.1.0" libc = "0.2" log = "0.4.8" -libgit2-sys = { path = "libgit2-sys", version = "0.16.2" } +libgit2-sys = { path = "libgit2-sys", version = "0.17.0" } [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] openssl-sys = { version = "0.9.45", optional = true } diff --git a/examples/fetch.rs b/examples/fetch.rs index f3a11dffbd..af4aa98eb9 100644 --- a/examples/fetch.rs +++ b/examples/fetch.rs @@ -15,7 +15,7 @@ #![deny(warnings)] use clap::Parser; -use git2::{AutotagOption, FetchOptions, RemoteCallbacks, Repository}; +use git2::{AutotagOption, FetchOptions, RemoteCallbacks, RemoteUpdateFlags, Repository}; use std::io::{self, Write}; use std::str; @@ -113,7 +113,12 @@ fn run(args: &Args) -> Result<(), git2::Error> { // commits. This may be needed even if there was no packfile to download, // which can happen e.g. when the branches have been changed but all the // needed objects are available locally. - remote.update_tips(None, true, AutotagOption::Unspecified, None)?; + remote.update_tips( + None, + RemoteUpdateFlags::UPDATE_FETCHHEAD, + AutotagOption::Unspecified, + None, + )?; Ok(()) } diff --git a/libgit2-sys/Cargo.toml b/libgit2-sys/Cargo.toml index 67eaf8dc8e..ff911c99f5 100644 --- a/libgit2-sys/Cargo.toml +++ b/libgit2-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libgit2-sys" -version = "0.16.2+1.7.2" +version = "0.17.0+1.8.1" authors = ["Josh Triplett ", "Alex Crichton "] links = "git2" build = "build.rs" diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 88fce00d6e..1abf628c6e 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -7,7 +7,7 @@ use std::process::Command; /// Tries to use system libgit2 and emits necessary build script instructions. fn try_system_libgit2() -> Result { let mut cfg = pkg_config::Config::new(); - match cfg.range_version("1.7.2".."1.8.0").probe("libgit2") { + match cfg.range_version("1.8.1".."1.9.0").probe("libgit2") { Ok(lib) => { for include in &lib.include_paths { println!("cargo:root={}", include.display()); @@ -89,9 +89,9 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN add_c_files(&mut cfg, "libgit2/src/libgit2/transports"); add_c_files(&mut cfg, "libgit2/src/libgit2/streams"); - // Always use bundled http-parser for now - cfg.include("libgit2/deps/http-parser") - .file("libgit2/deps/http-parser/http_parser.c"); + // Always use bundled HTTP parser (llhttp) for now + cfg.include("libgit2/deps/llhttp"); + add_c_files(&mut cfg, "libgit2/deps/llhttp"); // external/system xdiff is not yet supported cfg.include("libgit2/deps/xdiff"); @@ -150,6 +150,7 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN features.push_str("#define INCLUDE_features_h\n"); features.push_str("#define GIT_THREADS 1\n"); features.push_str("#define GIT_TRACE 1\n"); + features.push_str("#define GIT_HTTPPARSER_BUILTIN 1\n"); if !target.contains("android") { features.push_str("#define GIT_USE_NSEC 1\n"); @@ -180,7 +181,8 @@ The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VEN cfg.include(path); } features.push_str("#define GIT_SSH 1\n"); - features.push_str("#define GIT_SSH_MEMORY_CREDENTIALS 1\n"); + features.push_str("#define GIT_SSH_LIBSSH2 1\n"); + features.push_str("#define GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS 1\n"); } if https { features.push_str("#define GIT_HTTPS 1\n"); diff --git a/libgit2-sys/lib.rs b/libgit2-sys/lib.rs index 3de6881842..af1dff048b 100644 --- a/libgit2-sys/lib.rs +++ b/libgit2-sys/lib.rs @@ -198,6 +198,10 @@ git_enum! { GIT_EINDEXDIRTY = -34, GIT_EAPPLYFAIL = -35, GIT_EOWNER = -36, + GIT_TIMEOUT = -37, + GIT_EUNCHANGED = -38, + GIT_ENOTSUPPORTED = -39, + GIT_EREADONLY = -40, } } @@ -366,6 +370,13 @@ pub struct git_indexer_options { pub type git_remote_ready_cb = Option c_int>; +git_enum! { + pub enum git_remote_update_flags { + GIT_REMOTE_UPDATE_FETCHHEAD = 1 << 0, + GIT_REMOTE_UPDATE_REPORT_UNCHANGED = 1 << 1, + } +} + #[repr(C)] pub struct git_remote_callbacks { pub version: c_uint, @@ -391,7 +402,7 @@ pub struct git_fetch_options { pub version: c_int, pub callbacks: git_remote_callbacks, pub prune: git_fetch_prune_t, - pub update_fetchhead: c_int, + pub update_fetchhead: c_uint, pub download_tags: git_remote_autotag_option_t, pub proxy_opts: git_proxy_options, pub depth: c_int, @@ -855,10 +866,11 @@ pub struct git_index_time { pub struct git_config_entry { pub name: *const c_char, pub value: *const c_char, + pub backend_type: *const c_char, + pub origin_path: *const c_char, pub include_depth: c_uint, pub level: git_config_level_t, pub free: Option, - pub payload: *mut c_void, } git_enum! { @@ -868,7 +880,8 @@ git_enum! { GIT_CONFIG_LEVEL_XDG = 3, GIT_CONFIG_LEVEL_GLOBAL = 4, GIT_CONFIG_LEVEL_LOCAL = 5, - GIT_CONFIG_LEVEL_APP = 6, + GIT_CONFIG_LEVEL_WORKTREE = 6, + GIT_CONFIG_LEVEL_APP = 7, GIT_CONFIG_HIGHEST_LEVEL = -1, } } @@ -981,6 +994,7 @@ pub struct git_push_options { pub proxy_opts: git_proxy_options, pub follow_redirects: git_remote_redirect_t, pub custom_headers: git_strarray, + pub remote_push_options: git_strarray, } pub type git_tag_foreach_cb = @@ -1947,6 +1961,14 @@ git_enum! { GIT_OPT_SET_EXTENSIONS, GIT_OPT_GET_OWNER_VALIDATION, GIT_OPT_SET_OWNER_VALIDATION, + GIT_OPT_GET_HOMEDIR, + GIT_OPT_SET_HOMEDIR, + GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, + GIT_OPT_GET_SERVER_CONNECT_TIMEOUT, + GIT_OPT_SET_SERVER_TIMEOUT, + GIT_OPT_GET_SERVER_TIMEOUT, + GIT_OPT_SET_USER_AGENT_PRODUCT, + GIT_OPT_GET_USER_AGENT_PRODUCT, } } @@ -1963,6 +1985,7 @@ git_enum! { pub struct git_worktree_add_options { pub version: c_uint, pub lock: c_int, + pub checkout_existing: c_int, pub reference: *mut git_reference, pub checkout_options: git_checkout_options, } @@ -2326,7 +2349,7 @@ extern "C" { pub fn git_remote_update_tips( remote: *mut git_remote, callbacks: *const git_remote_callbacks, - update_fetchead: c_int, + update_flags: c_uint, download_tags: git_remote_autotag_option_t, reflog_message: *const c_char, ) -> c_int; @@ -2882,7 +2905,7 @@ extern "C" { message: *const c_char, tree: *const git_tree, parent_count: size_t, - parents: *mut *const git_commit, + parents: *const *mut git_commit, ) -> c_int; pub fn git_commit_create_buffer( out: *mut git_buf, @@ -2893,7 +2916,7 @@ extern "C" { message: *const c_char, tree: *const git_tree, parent_count: size_t, - parents: *mut *const git_commit, + parents: *const *mut git_commit, ) -> c_int; pub fn git_commit_header_field( out: *mut git_buf, diff --git a/libgit2-sys/libgit2 b/libgit2-sys/libgit2 index a418d9d4ab..36f7e21ad7 160000 --- a/libgit2-sys/libgit2 +++ b/libgit2-sys/libgit2 @@ -1 +1 @@ -Subproject commit a418d9d4ab87bae16b87d8f37143a4687ae0e4b2 +Subproject commit 36f7e21ad757a3dacc58cf7944329da6bc1d6e96 diff --git a/src/call.rs b/src/call.rs index 95350d2690..a18f05da91 100644 --- a/src/call.rs +++ b/src/call.rs @@ -166,6 +166,7 @@ mod impls { ConfigLevel::XDG => raw::GIT_CONFIG_LEVEL_XDG, ConfigLevel::Global => raw::GIT_CONFIG_LEVEL_GLOBAL, ConfigLevel::Local => raw::GIT_CONFIG_LEVEL_LOCAL, + ConfigLevel::Worktree => raw::GIT_CONFIG_LEVEL_WORKTREE, ConfigLevel::App => raw::GIT_CONFIG_LEVEL_APP, ConfigLevel::Highest => raw::GIT_CONFIG_HIGHEST_LEVEL, } diff --git a/src/cred.rs b/src/cred.rs index fc4af1e4eb..fc0b941935 100644 --- a/src/cred.rs +++ b/src/cred.rs @@ -482,7 +482,7 @@ mod test { macro_rules! test_cfg( ($($k:expr => $v:expr),*) => ({ let td = TempDir::new().unwrap(); let mut cfg = Config::new().unwrap(); - cfg.add_file(&td.path().join("cfg"), ConfigLevel::Highest, false).unwrap(); + cfg.add_file(&td.path().join("cfg"), ConfigLevel::App, false).unwrap(); $(cfg.set_str($k, $v).unwrap();)* cfg }) ); diff --git a/src/lib.rs b/src/lib.rs index 3dd6fe92ee..689dcfb37e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -385,6 +385,8 @@ pub enum ConfigLevel { Global, /// Repository specific config, e.g. $PWD/.git/config Local, + /// Worktree specific configuration file, e.g. $GIT_DIR/config.worktree + Worktree, /// Application specific configuration file App, /// Highest level available @@ -662,6 +664,17 @@ bitflags! { } } +bitflags! { + /// How to handle reference updates. + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct RemoteUpdateFlags: u32 { + /// Write the fetch results to FETCH_HEAD. + const UPDATE_FETCHHEAD = raw::GIT_REMOTE_UPDATE_FETCHHEAD as u32; + /// Report unchanged tips in the update_tips callback. + const REPORT_UNCHANGED = raw::GIT_REMOTE_UPDATE_REPORT_UNCHANGED as u32; + } +} + #[cfg(test)] #[macro_use] mod test; @@ -963,6 +976,7 @@ impl ConfigLevel { raw::GIT_CONFIG_LEVEL_XDG => ConfigLevel::XDG, raw::GIT_CONFIG_LEVEL_GLOBAL => ConfigLevel::Global, raw::GIT_CONFIG_LEVEL_LOCAL => ConfigLevel::Local, + raw::GIT_CONFIG_LEVEL_WORKTREE => ConfigLevel::Worktree, raw::GIT_CONFIG_LEVEL_APP => ConfigLevel::App, raw::GIT_CONFIG_HIGHEST_LEVEL => ConfigLevel::Highest, n => panic!("unknown config level: {}", n), diff --git a/src/remote.rs b/src/remote.rs index a15a095010..13c275ae2e 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -3,6 +3,7 @@ use std::iter::FusedIterator; use std::marker; use std::mem; use std::ops::Range; +use std::os::raw::c_uint; use std::ptr; use std::slice; use std::str; @@ -11,7 +12,7 @@ use std::{ffi::CString, os::raw::c_char}; use crate::string_array::StringArray; use crate::util::Binding; use crate::{call, raw, Buf, Direction, Error, FetchPrune, Oid, ProxyOptions, Refspec}; -use crate::{AutotagOption, Progress, RemoteCallbacks, Repository}; +use crate::{AutotagOption, Progress, RemoteCallbacks, RemoteUpdateFlags, Repository}; /// A structure representing a [remote][1] of a git repository. /// @@ -43,7 +44,7 @@ pub struct FetchOptions<'cb> { depth: i32, proxy: Option>, prune: FetchPrune, - update_fetchhead: bool, + update_flags: RemoteUpdateFlags, download_tags: AutotagOption, follow_redirects: RemoteRedirect, custom_headers: Vec, @@ -58,6 +59,8 @@ pub struct PushOptions<'cb> { follow_redirects: RemoteRedirect, custom_headers: Vec, custom_headers_ptrs: Vec<*const c_char>, + remote_push_options: Vec, + remote_push_options_ptrs: Vec<*const c_char>, } /// Holds callbacks for a connection to a `Remote`. Disconnects when dropped @@ -318,7 +321,7 @@ impl<'repo> Remote<'repo> { pub fn update_tips( &mut self, callbacks: Option<&mut RemoteCallbacks<'_>>, - update_fetchhead: bool, + update_flags: RemoteUpdateFlags, download_tags: AutotagOption, msg: Option<&str>, ) -> Result<(), Error> { @@ -328,7 +331,7 @@ impl<'repo> Remote<'repo> { try_call!(raw::git_remote_update_tips( self.raw, cbs.as_ref(), - update_fetchhead, + update_flags.bits() as c_uint, download_tags, msg )); @@ -504,7 +507,7 @@ impl<'cb> FetchOptions<'cb> { callbacks: None, proxy: None, prune: FetchPrune::Unspecified, - update_fetchhead: true, + update_flags: RemoteUpdateFlags::UPDATE_FETCHHEAD, download_tags: AutotagOption::Unspecified, follow_redirects: RemoteRedirect::Initial, custom_headers: Vec::new(), @@ -535,7 +538,17 @@ impl<'cb> FetchOptions<'cb> { /// /// Defaults to `true`. pub fn update_fetchhead(&mut self, update: bool) -> &mut Self { - self.update_fetchhead = update; + self.update_flags + .set(RemoteUpdateFlags::UPDATE_FETCHHEAD, update); + self + } + + /// Set whether to report unchanged tips in the update_tips callback. + /// + /// Defaults to `false`. + pub fn report_unchanged(&mut self, update: bool) -> &mut Self { + self.update_flags + .set(RemoteUpdateFlags::REPORT_UNCHANGED, update); self } @@ -600,7 +613,10 @@ impl<'cb> Binding for FetchOptions<'cb> { .map(|m| m.raw()) .unwrap_or_else(|| ProxyOptions::new().raw()), prune: crate::call::convert(&self.prune), - update_fetchhead: crate::call::convert(&self.update_fetchhead), + // `update_fetchhead` is an incorrectly named option which contains both + // the `UPDATE_FETCHHEAD` and `REPORT_UNCHANGED` flags. + // See https://github.com/libgit2/libgit2/pull/6806 + update_fetchhead: self.update_flags.bits() as c_uint, download_tags: crate::call::convert(&self.download_tags), depth: self.depth, follow_redirects: self.follow_redirects.raw(), @@ -628,6 +644,8 @@ impl<'cb> PushOptions<'cb> { follow_redirects: RemoteRedirect::Initial, custom_headers: Vec::new(), custom_headers_ptrs: Vec::new(), + remote_push_options: Vec::new(), + remote_push_options_ptrs: Vec::new(), } } @@ -673,6 +691,20 @@ impl<'cb> PushOptions<'cb> { self.custom_headers_ptrs = self.custom_headers.iter().map(|s| s.as_ptr()).collect(); self } + + /// Set "push options" to deliver to the remote. + pub fn remote_push_options(&mut self, remote_push_options: &[&str]) -> &mut Self { + self.remote_push_options = remote_push_options + .iter() + .map(|&s| CString::new(s).unwrap()) + .collect(); + self.remote_push_options_ptrs = self + .remote_push_options + .iter() + .map(|s| s.as_ptr()) + .collect(); + self + } } impl<'cb> Binding for PushOptions<'cb> { @@ -700,6 +732,10 @@ impl<'cb> Binding for PushOptions<'cb> { count: self.custom_headers_ptrs.len(), strings: self.custom_headers_ptrs.as_ptr() as *mut _, }, + remote_push_options: git_strarray { + count: self.remote_push_options.len(), + strings: self.remote_push_options_ptrs.as_ptr() as *mut _, + }, } } } @@ -756,7 +792,7 @@ impl RemoteRedirect { #[cfg(test)] mod tests { - use crate::{AutotagOption, PushOptions}; + use crate::{AutotagOption, PushOptions, RemoteUpdateFlags}; use crate::{Direction, FetchOptions, Remote, RemoteCallbacks, Repository}; use std::cell::Cell; use tempfile::TempDir; @@ -845,10 +881,20 @@ mod tests { origin.fetch(&[] as &[&str], None, None).unwrap(); origin.fetch(&[] as &[&str], None, Some("foo")).unwrap(); origin - .update_tips(None, true, AutotagOption::Unspecified, None) + .update_tips( + None, + RemoteUpdateFlags::UPDATE_FETCHHEAD, + AutotagOption::Unspecified, + None, + ) .unwrap(); origin - .update_tips(None, true, AutotagOption::All, Some("foo")) + .update_tips( + None, + RemoteUpdateFlags::UPDATE_FETCHHEAD, + AutotagOption::All, + Some("foo"), + ) .unwrap(); t!(repo.remote_add_fetch("origin", "foo")); diff --git a/src/repo.rs b/src/repo.rs index b41b42480c..db00545e4f 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -1290,9 +1290,9 @@ impl Repository { parents: &[&Commit<'_>], ) -> Result { let update_ref = crate::opt_cstr(update_ref)?; - let mut parent_ptrs = parents + let parent_ptrs = parents .iter() - .map(|p| p.raw() as *const raw::git_commit) + .map(|p| p.raw() as *mut raw::git_commit) .collect::>(); let message = CString::new(message)?; let mut raw = raw::git_oid { @@ -1309,7 +1309,7 @@ impl Repository { message, tree.raw(), parents.len() as size_t, - parent_ptrs.as_mut_ptr() + parent_ptrs.as_ptr() )); Ok(Binding::from_raw(&raw as *const _)) } @@ -1328,9 +1328,9 @@ impl Repository { tree: &Tree<'_>, parents: &[&Commit<'_>], ) -> Result { - let mut parent_ptrs = parents + let parent_ptrs = parents .iter() - .map(|p| p.raw() as *const raw::git_commit) + .map(|p| p.raw() as *mut raw::git_commit) .collect::>(); let message = CString::new(message)?; let buf = Buf::new(); @@ -1344,7 +1344,7 @@ impl Repository { message, tree.raw(), parents.len() as size_t, - parent_ptrs.as_mut_ptr() + parent_ptrs.as_ptr() )); Ok(buf) } diff --git a/src/test.rs b/src/test.rs index c1ff1de21f..57a590f519 100644 --- a/src/test.rs +++ b/src/test.rs @@ -66,7 +66,7 @@ pub fn worktrees_env_init(repo: &Repository) -> (TempDir, Branch<'_>) { #[cfg(windows)] pub fn realpath(original: &Path) -> io::Result { - Ok(original.to_path_buf()) + Ok(original.canonicalize()?.to_path_buf()) } #[cfg(unix)] pub fn realpath(original: &Path) -> io::Result { diff --git a/src/worktree.rs b/src/worktree.rs index 569b639cf9..fc32902db1 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -165,6 +165,12 @@ impl<'a> WorktreeAddOptions<'a> { self } + /// If enabled, this will checkout the existing branch matching the worktree name. + pub fn checkout_existing(&mut self, enabled: bool) -> &mut WorktreeAddOptions<'a> { + self.raw.checkout_existing = enabled as c_int; + self + } + /// reference to use for the new worktree HEAD pub fn reference( &mut self, diff --git a/systest/build.rs b/systest/build.rs index bb34d9de80..85e8b4b437 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -7,6 +7,7 @@ fn main() { cfg.include(PathBuf::from(root).join("include")); } cfg.header("git2.h") + .header("git2/sys/errors.h") .header("git2/sys/transport.h") .header("git2/sys/refs.h") .header("git2/sys/refdb_backend.h") diff --git a/tests/add_extensions.rs b/tests/add_extensions.rs index 57c0eb9762..f3e2c42ae3 100644 --- a/tests/add_extensions.rs +++ b/tests/add_extensions.rs @@ -11,11 +11,13 @@ fn test_add_extensions() -> Result<(), Error> { let extensions = unsafe { get_extensions() }?; - assert_eq!(extensions.len(), 3); + assert_eq!(extensions.len(), 4); assert_eq!(extensions.get(0), Some("custom")); - // The objectformat extension was added in 1.6 assert_eq!(extensions.get(1), Some("noop")); + // The objectformat extension was added in 1.6 assert_eq!(extensions.get(2), Some("objectformat")); + // The worktreeconfig extension was added in 1.8 + assert_eq!(extensions.get(3), Some("worktreeconfig")); Ok(()) } diff --git a/tests/get_extensions.rs b/tests/get_extensions.rs index d8dd55fe0a..3a9148f20b 100644 --- a/tests/get_extensions.rs +++ b/tests/get_extensions.rs @@ -7,10 +7,12 @@ use git2::Error; fn test_get_extensions() -> Result<(), Error> { let extensions = unsafe { get_extensions() }?; - assert_eq!(extensions.len(), 2); + assert_eq!(extensions.len(), 3); assert_eq!(extensions.get(0), Some("noop")); // The objectformat extension was added in 1.6 assert_eq!(extensions.get(1), Some("objectformat")); + // The worktreeconfig extension was added in 1.8 + assert_eq!(extensions.get(2), Some("worktreeconfig")); Ok(()) } diff --git a/tests/remove_extensions.rs b/tests/remove_extensions.rs index 5f632a8809..5384daea5c 100644 --- a/tests/remove_extensions.rs +++ b/tests/remove_extensions.rs @@ -6,7 +6,14 @@ use git2::Error; #[test] fn test_remove_extensions() -> Result<(), Error> { unsafe { - set_extensions(&["custom", "!ignore", "!noop", "!objectformat", "other"])?; + set_extensions(&[ + "custom", + "!ignore", + "!noop", + "!objectformat", + "!worktreeconfig", + "other", + ])?; } let extensions = unsafe { get_extensions() }?;