//! # libgit2 bindings for Rust //! //! This library contains bindings to the [libgit2][1] C library which is used //! to manage git repositories. The library itself is a work in progress and is //! likely lacking some bindings here and there, so be warned. //! //! [1]: https://libgit2.github.com/ //! //! The git2-rs library strives to be as close to libgit2 as possible, but also //! strives to make using libgit2 as safe as possible. All resource management //! is automatic as well as adding strong types to all interfaces (including //! `Result`) //! //! ## Creating a `Repository` //! //! The `Repository` is the source from which almost all other objects in git-rs //! are spawned. A repository can be created through opening, initializing, or //! cloning. //! //! ### Initializing a new repository //! //! The `init` method will create a new repository, assuming one does not //! already exist. //! //! ```no_run //! # #![allow(unstable)] //! use git2::Repository; //! //! let repo = match Repository::init("/path/to/a/repo") { //! Ok(repo) => repo, //! Err(e) => panic!("failed to init: {}", e), //! }; //! ``` //! //! ### Opening an existing repository //! //! ```no_run //! # #![allow(unstable)] //! use git2::Repository; //! //! let repo = match Repository::open("/path/to/a/repo") { //! Ok(repo) => repo, //! Err(e) => panic!("failed to open: {}", e), //! }; //! ``` //! //! ### Cloning an existing repository //! //! ```no_run //! # #![allow(unstable)] //! use git2::Repository; //! //! let url = "https://github.com/alexcrichton/git2-rs"; //! let repo = match Repository::clone(url, "/path/to/a/repo") { //! Ok(repo) => repo, //! Err(e) => panic!("failed to clone: {}", e), //! }; //! ``` //! //! To clone using SSH, refer to [RepoBuilder](./build/struct.RepoBuilder.html). //! //! ## Working with a `Repository` //! //! All derivative objects, references, etc are attached to the lifetime of the //! source `Repository`, to ensure that they do not outlive the repository //! itself. #![doc(html_root_url = "https://docs.rs/git2/0.18")] #![allow(trivial_numeric_casts, trivial_casts)] #![deny(missing_docs)] #![warn(rust_2018_idioms)] #![cfg_attr(test, deny(warnings))] use bitflags::bitflags; use libgit2_sys as raw; use std::ffi::{CStr, CString}; use std::fmt; use std::str; use std::sync::Once; pub use crate::apply::{ApplyLocation, ApplyOptions}; pub use crate::attr::AttrValue; pub use crate::blame::{Blame, BlameHunk, BlameIter, BlameOptions}; pub use crate::blob::{Blob, BlobWriter}; pub use crate::branch::{Branch, Branches}; pub use crate::buf::Buf; pub use crate::cherrypick::CherrypickOptions; pub use crate::commit::{Commit, Parents}; pub use crate::config::{Config, ConfigEntries, ConfigEntry}; pub use crate::cred::{Cred, CredentialHelper}; pub use crate::describe::{Describe, DescribeFormatOptions, DescribeOptions}; pub use crate::diff::{Deltas, Diff, DiffDelta, DiffFile, DiffOptions}; pub use crate::diff::{DiffBinary, DiffBinaryFile, DiffBinaryKind}; pub use crate::diff::{DiffFindOptions, DiffHunk, DiffLine, DiffLineType, DiffStats}; pub use crate::email::{Email, EmailCreateOptions}; pub use crate::error::Error; pub use crate::index::{ Index, IndexConflict, IndexConflicts, IndexEntries, IndexEntry, IndexMatchedPath, }; pub use crate::indexer::{Indexer, IndexerProgress, Progress}; pub use crate::mailmap::Mailmap; pub use crate::mempack::Mempack; pub use crate::merge::{AnnotatedCommit, MergeOptions}; pub use crate::message::{ message_prettify, message_trailers_bytes, message_trailers_strs, MessageTrailersBytes, MessageTrailersBytesIterator, MessageTrailersStrs, MessageTrailersStrsIterator, DEFAULT_COMMENT_CHAR, }; pub use crate::note::{Note, Notes}; pub use crate::object::Object; pub use crate::odb::{Odb, OdbObject, OdbPackwriter, OdbReader, OdbWriter}; pub use crate::oid::Oid; pub use crate::packbuilder::{PackBuilder, PackBuilderStage}; pub use crate::patch::Patch; pub use crate::pathspec::{Pathspec, PathspecFailedEntries, PathspecMatchList}; pub use crate::pathspec::{PathspecDiffEntries, PathspecEntries}; pub use crate::proxy_options::ProxyOptions; pub use crate::push_update::PushUpdate; pub use crate::rebase::{Rebase, RebaseOperation, RebaseOperationType, RebaseOptions}; pub use crate::reference::{Reference, ReferenceNames, References}; pub use crate::reflog::{Reflog, ReflogEntry, ReflogIter}; pub use crate::refspec::Refspec; pub use crate::remote::{ FetchOptions, PushOptions, Refspecs, Remote, RemoteConnection, RemoteHead, RemoteRedirect, }; pub use crate::remote_callbacks::{CertificateCheckStatus, Credentials, RemoteCallbacks}; pub use crate::remote_callbacks::{TransportMessage, UpdateTips}; pub use crate::repo::{Repository, RepositoryInitOptions}; pub use crate::revert::RevertOptions; pub use crate::revspec::Revspec; pub use crate::revwalk::Revwalk; pub use crate::signature::Signature; pub use crate::stash::{StashApplyOptions, StashApplyProgressCb, StashCb}; pub use crate::status::{StatusEntry, StatusIter, StatusOptions, StatusShow, Statuses}; pub use crate::submodule::{Submodule, SubmoduleUpdateOptions}; pub use crate::tag::Tag; pub use crate::time::{IndexTime, Time}; pub use crate::tracing::{trace_set, TraceLevel}; pub use crate::transaction::Transaction; pub use crate::tree::{Tree, TreeEntry, TreeIter, TreeWalkMode, TreeWalkResult}; pub use crate::treebuilder::TreeBuilder; pub use crate::util::IntoCString; pub use crate::version::Version; pub use crate::worktree::{Worktree, WorktreeAddOptions, WorktreeLockStatus, WorktreePruneOptions}; // Create a convinience method on bitflag struct which checks the given flag macro_rules! is_bit_set { ($name:ident, $flag:expr) => { #[allow(missing_docs)] pub fn $name(&self) -> bool { self.intersects($flag) } }; } /// An enumeration of possible errors that can happen when working with a git /// repository. // Note: We omit a few native error codes, as they are unlikely to be propagated // to the library user. Currently: // // * GIT_EPASSTHROUGH // * GIT_ITEROVER // * GIT_RETRY #[derive(PartialEq, Eq, Clone, Debug, Copy)] pub enum ErrorCode { /// Generic error GenericError, /// Requested object could not be found NotFound, /// Object exists preventing operation Exists, /// More than one object matches Ambiguous, /// Output buffer too short to hold data BufSize, /// User-generated error User, /// Operation not allowed on bare repository BareRepo, /// HEAD refers to branch with no commits UnbornBranch, /// Merge in progress prevented operation Unmerged, /// Reference was not fast-forwardable NotFastForward, /// Name/ref spec was not in a valid format InvalidSpec, /// Checkout conflicts prevented operation Conflict, /// Lock file prevented operation Locked, /// Reference value does not match expected Modified, /// Authentication error Auth, /// Server certificate is invalid Certificate, /// Patch/merge has already been applied Applied, /// The requested peel operation is not possible Peel, /// Unexpected EOF Eof, /// Invalid operation or input Invalid, /// Uncommitted changes in index prevented operation Uncommitted, /// Operation was not valid for a directory Directory, /// A merge conflict exists and cannot continue MergeConflict, /// Hashsum mismatch in object HashsumMismatch, /// Unsaved changes in the index would be overwritten IndexDirty, /// Patch application failed ApplyFail, /// The object is not owned by the current user Owner, } /// An enumeration of possible categories of things that can have /// errors when working with a git repository. #[derive(PartialEq, Eq, Clone, Debug, Copy)] pub enum ErrorClass { /// Uncategorized None, /// Out of memory or insufficient allocated space NoMemory, /// Syscall or standard system library error Os, /// Invalid input Invalid, /// Error resolving or manipulating a reference Reference, /// ZLib failure Zlib, /// Bad repository state Repository, /// Bad configuration Config, /// Regex failure Regex, /// Bad object Odb, /// Invalid index data Index, /// Error creating or obtaining an object Object, /// Network error Net, /// Error manipulating a tag Tag, /// Invalid value in tree Tree, /// Hashing or packing error Indexer, /// Error from SSL Ssl, /// Error involving submodules Submodule, /// Threading error Thread, /// Error manipulating a stash Stash, /// Checkout failure Checkout, /// Invalid FETCH_HEAD FetchHead, /// Merge failure Merge, /// SSH failure Ssh, /// Error manipulating filters Filter, /// Error reverting commit Revert, /// Error from a user callback Callback, /// Error cherry-picking commit CherryPick, /// Can't describe object Describe, /// Error during rebase Rebase, /// Filesystem-related error Filesystem, /// Invalid patch data Patch, /// Error involving worktrees Worktree, /// Hash library error or SHA-1 collision Sha1, /// HTTP error Http, } /// A listing of the possible states that a repository can be in. #[derive(PartialEq, Eq, Clone, Debug, Copy)] #[allow(missing_docs)] pub enum RepositoryState { Clean, Merge, Revert, RevertSequence, CherryPick, CherryPickSequence, Bisect, Rebase, RebaseInteractive, RebaseMerge, ApplyMailbox, ApplyMailboxOrRebase, } /// An enumeration of the possible directions for a remote. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Direction { /// Data will be fetched (read) from this remote. Fetch, /// Data will be pushed (written) to this remote. Push, } /// An enumeration of the operations that can be performed for the `reset` /// method on a `Repository`. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ResetType { /// Move the head to the given commit. Soft, /// Soft plus reset the index to the commit. Mixed, /// Mixed plus changes in the working tree are discarded. Hard, } /// An enumeration all possible kinds objects may have. #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum ObjectType { /// Any kind of git object Any, /// An object which corresponds to a git commit Commit, /// An object which corresponds to a git tree Tree, /// An object which corresponds to a git blob Blob, /// An object which corresponds to a git tag Tag, } /// An enumeration of all possible kinds of references. #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum ReferenceType { /// A reference which points at an object id. Direct, /// A reference which points at another reference. Symbolic, } /// An enumeration for the possible types of branches #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum BranchType { /// A local branch not on a remote. Local, /// A branch for a remote. Remote, } /// An enumeration of the possible priority levels of a config file. /// /// The levels corresponding to the escalation logic (higher to lower) when /// searching for config entries. #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum ConfigLevel { /// System-wide on Windows, for compatibility with portable git ProgramData = 1, /// System-wide configuration file, e.g. /etc/gitconfig System, /// XDG-compatible configuration file, e.g. ~/.config/git/config XDG, /// User-specific configuration, e.g. ~/.gitconfig Global, /// Repository specific config, e.g. $PWD/.git/config Local, /// Application specific configuration file App, /// Highest level available Highest = -1, } /// Merge file favor options for `MergeOptions` instruct the file-level /// merging functionality how to deal with conflicting regions of the files. #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum FileFavor { /// When a region of a file is changed in both branches, a conflict will be /// recorded in the index so that git_checkout can produce a merge file with /// conflict markers in the working directory. This is the default. Normal, /// When a region of a file is changed in both branches, the file created /// in the index will contain the "ours" side of any conflicting region. /// The index will not record a conflict. Ours, /// When a region of a file is changed in both branches, the file created /// in the index will contain the "theirs" side of any conflicting region. /// The index will not record a conflict. Theirs, /// When a region of a file is changed in both branches, the file created /// in the index will contain each unique line from each side, which has /// the result of combining both files. The index will not record a conflict. Union, } bitflags! { /// Orderings that may be specified for Revwalk iteration. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Sort: u32 { /// Sort the repository contents in no particular ordering. /// /// This sorting is arbitrary, implementation-specific, and subject to /// change at any time. This is the default sorting for new walkers. const NONE = raw::GIT_SORT_NONE as u32; /// Sort the repository contents in topological order (children before /// parents). /// /// This sorting mode can be combined with time sorting. const TOPOLOGICAL = raw::GIT_SORT_TOPOLOGICAL as u32; /// Sort the repository contents by commit time. /// /// This sorting mode can be combined with topological sorting. const TIME = raw::GIT_SORT_TIME as u32; /// Iterate through the repository contents in reverse order. /// /// This sorting mode can be combined with any others. const REVERSE = raw::GIT_SORT_REVERSE as u32; } } impl Sort { is_bit_set!(is_none, Sort::NONE); is_bit_set!(is_topological, Sort::TOPOLOGICAL); is_bit_set!(is_time, Sort::TIME); is_bit_set!(is_reverse, Sort::REVERSE); } bitflags! { /// Types of credentials that can be requested by a credential callback. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct CredentialType: u32 { #[allow(missing_docs)] const USER_PASS_PLAINTEXT = raw::GIT_CREDTYPE_USERPASS_PLAINTEXT as u32; #[allow(missing_docs)] const SSH_KEY = raw::GIT_CREDTYPE_SSH_KEY as u32; #[allow(missing_docs)] const SSH_MEMORY = raw::GIT_CREDTYPE_SSH_MEMORY as u32; #[allow(missing_docs)] const SSH_CUSTOM = raw::GIT_CREDTYPE_SSH_CUSTOM as u32; #[allow(missing_docs)] const DEFAULT = raw::GIT_CREDTYPE_DEFAULT as u32; #[allow(missing_docs)] const SSH_INTERACTIVE = raw::GIT_CREDTYPE_SSH_INTERACTIVE as u32; #[allow(missing_docs)] const USERNAME = raw::GIT_CREDTYPE_USERNAME as u32; } } impl CredentialType { is_bit_set!(is_user_pass_plaintext, CredentialType::USER_PASS_PLAINTEXT); is_bit_set!(is_ssh_key, CredentialType::SSH_KEY); is_bit_set!(is_ssh_memory, CredentialType::SSH_MEMORY); is_bit_set!(is_ssh_custom, CredentialType::SSH_CUSTOM); is_bit_set!(is_default, CredentialType::DEFAULT); is_bit_set!(is_ssh_interactive, CredentialType::SSH_INTERACTIVE); is_bit_set!(is_username, CredentialType::USERNAME); } impl Default for CredentialType { fn default() -> Self { CredentialType::DEFAULT } } bitflags! { /// Flags for the `flags` field of an IndexEntry. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct IndexEntryFlag: u16 { /// Set when the `extended_flags` field is valid. const EXTENDED = raw::GIT_INDEX_ENTRY_EXTENDED as u16; /// "Assume valid" flag const VALID = raw::GIT_INDEX_ENTRY_VALID as u16; } } impl IndexEntryFlag { is_bit_set!(is_extended, IndexEntryFlag::EXTENDED); is_bit_set!(is_valid, IndexEntryFlag::VALID); } bitflags! { /// Flags for the `extended_flags` field of an IndexEntry. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct IndexEntryExtendedFlag: u16 { /// An "intent to add" entry from "git add -N" const INTENT_TO_ADD = raw::GIT_INDEX_ENTRY_INTENT_TO_ADD as u16; /// Skip the associated worktree file, for sparse checkouts const SKIP_WORKTREE = raw::GIT_INDEX_ENTRY_SKIP_WORKTREE as u16; #[allow(missing_docs)] const UPTODATE = raw::GIT_INDEX_ENTRY_UPTODATE as u16; } } impl IndexEntryExtendedFlag { is_bit_set!(is_intent_to_add, IndexEntryExtendedFlag::INTENT_TO_ADD); is_bit_set!(is_skip_worktree, IndexEntryExtendedFlag::SKIP_WORKTREE); is_bit_set!(is_up_to_date, IndexEntryExtendedFlag::UPTODATE); } bitflags! { /// Flags for APIs that add files matching pathspec #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct IndexAddOption: u32 { #[allow(missing_docs)] const DEFAULT = raw::GIT_INDEX_ADD_DEFAULT as u32; #[allow(missing_docs)] const FORCE = raw::GIT_INDEX_ADD_FORCE as u32; #[allow(missing_docs)] const DISABLE_PATHSPEC_MATCH = raw::GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH as u32; #[allow(missing_docs)] const CHECK_PATHSPEC = raw::GIT_INDEX_ADD_CHECK_PATHSPEC as u32; } } impl IndexAddOption { is_bit_set!(is_default, IndexAddOption::DEFAULT); is_bit_set!(is_force, IndexAddOption::FORCE); is_bit_set!( is_disable_pathspec_match, IndexAddOption::DISABLE_PATHSPEC_MATCH ); is_bit_set!(is_check_pathspec, IndexAddOption::CHECK_PATHSPEC); } impl Default for IndexAddOption { fn default() -> Self { IndexAddOption::DEFAULT } } bitflags! { /// Flags for `Repository::open_ext` #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RepositoryOpenFlags: u32 { /// Only open the specified path; don't walk upward searching. const NO_SEARCH = raw::GIT_REPOSITORY_OPEN_NO_SEARCH as u32; /// Search across filesystem boundaries. const CROSS_FS = raw::GIT_REPOSITORY_OPEN_CROSS_FS as u32; /// Force opening as bare repository, and defer loading its config. const BARE = raw::GIT_REPOSITORY_OPEN_BARE as u32; /// Don't try appending `/.git` to the specified repository path. const NO_DOTGIT = raw::GIT_REPOSITORY_OPEN_NO_DOTGIT as u32; /// Respect environment variables like `$GIT_DIR`. const FROM_ENV = raw::GIT_REPOSITORY_OPEN_FROM_ENV as u32; } } impl RepositoryOpenFlags { is_bit_set!(is_no_search, RepositoryOpenFlags::NO_SEARCH); is_bit_set!(is_cross_fs, RepositoryOpenFlags::CROSS_FS); is_bit_set!(is_bare, RepositoryOpenFlags::BARE); is_bit_set!(is_no_dotgit, RepositoryOpenFlags::NO_DOTGIT); is_bit_set!(is_from_env, RepositoryOpenFlags::FROM_ENV); } bitflags! { /// Flags for the return value of `Repository::revparse` #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RevparseMode: u32 { /// The spec targeted a single object const SINGLE = raw::GIT_REVPARSE_SINGLE as u32; /// The spec targeted a range of commits const RANGE = raw::GIT_REVPARSE_RANGE as u32; /// The spec used the `...` operator, which invokes special semantics. const MERGE_BASE = raw::GIT_REVPARSE_MERGE_BASE as u32; } } impl RevparseMode { is_bit_set!(is_no_single, RevparseMode::SINGLE); is_bit_set!(is_range, RevparseMode::RANGE); is_bit_set!(is_merge_base, RevparseMode::MERGE_BASE); } bitflags! { /// The results of `merge_analysis` indicating the merge opportunities. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct MergeAnalysis: u32 { /// No merge is possible. const ANALYSIS_NONE = raw::GIT_MERGE_ANALYSIS_NONE as u32; /// A "normal" merge; both HEAD and the given merge input have diverged /// from their common ancestor. The divergent commits must be merged. const ANALYSIS_NORMAL = raw::GIT_MERGE_ANALYSIS_NORMAL as u32; /// All given merge inputs are reachable from HEAD, meaning the /// repository is up-to-date and no merge needs to be performed. const ANALYSIS_UP_TO_DATE = raw::GIT_MERGE_ANALYSIS_UP_TO_DATE as u32; /// The given merge input is a fast-forward from HEAD and no merge /// needs to be performed. Instead, the client can check out the /// given merge input. const ANALYSIS_FASTFORWARD = raw::GIT_MERGE_ANALYSIS_FASTFORWARD as u32; /// The HEAD of the current repository is "unborn" and does not point to /// a valid commit. No merge can be performed, but the caller may wish /// to simply set HEAD to the target commit(s). const ANALYSIS_UNBORN = raw::GIT_MERGE_ANALYSIS_UNBORN as u32; } } impl MergeAnalysis { is_bit_set!(is_none, MergeAnalysis::ANALYSIS_NONE); is_bit_set!(is_normal, MergeAnalysis::ANALYSIS_NORMAL); is_bit_set!(is_up_to_date, MergeAnalysis::ANALYSIS_UP_TO_DATE); is_bit_set!(is_fast_forward, MergeAnalysis::ANALYSIS_FASTFORWARD); is_bit_set!(is_unborn, MergeAnalysis::ANALYSIS_UNBORN); } bitflags! { /// The user's stated preference for merges. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct MergePreference: u32 { /// No configuration was found that suggests a preferred behavior for /// merge. const NONE = raw::GIT_MERGE_PREFERENCE_NONE as u32; /// There is a `merge.ff=false` configuration setting, suggesting that /// the user does not want to allow a fast-forward merge. const NO_FAST_FORWARD = raw::GIT_MERGE_PREFERENCE_NO_FASTFORWARD as u32; /// There is a `merge.ff=only` configuration setting, suggesting that /// the user only wants fast-forward merges. const FASTFORWARD_ONLY = raw::GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY as u32; } } impl MergePreference { is_bit_set!(is_none, MergePreference::NONE); is_bit_set!(is_no_fast_forward, MergePreference::NO_FAST_FORWARD); is_bit_set!(is_fastforward_only, MergePreference::FASTFORWARD_ONLY); } bitflags! { /// Flags controlling the behavior of ODB lookup operations #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct OdbLookupFlags: u32 { /// Don't call `git_odb_refresh` if the lookup fails. Useful when doing /// a batch of lookup operations for objects that may legitimately not /// exist. When using this flag, you may wish to manually call /// `git_odb_refresh` before processing a batch of objects. const NO_REFRESH = raw::GIT_ODB_LOOKUP_NO_REFRESH as u32; } } #[cfg(test)] #[macro_use] mod test; #[macro_use] mod panic; mod attr; mod call; mod util; pub mod build; pub mod cert; pub mod oid_array; pub mod opts; pub mod string_array; pub mod transport; mod apply; mod blame; mod blob; mod branch; mod buf; mod cherrypick; mod commit; mod config; mod cred; mod describe; mod diff; mod email; mod error; mod index; mod indexer; mod mailmap; mod mempack; mod merge; mod message; mod note; mod object; mod odb; mod oid; mod packbuilder; mod patch; mod pathspec; mod proxy_options; mod push_update; mod rebase; mod reference; mod reflog; mod refspec; mod remote; mod remote_callbacks; mod repo; mod revert; mod revspec; mod revwalk; mod signature; mod stash; mod status; mod submodule; mod tag; mod tagforeach; mod time; mod tracing; mod transaction; mod tree; mod treebuilder; mod version; mod worktree; fn init() { static INIT: Once = Once::new(); INIT.call_once(|| { openssl_env_init(); }); raw::init(); } #[cfg(all( unix, not(target_os = "macos"), not(target_os = "ios"), feature = "https" ))] fn openssl_env_init() { // Currently, libgit2 leverages OpenSSL for SSL support when cloning // repositories over HTTPS. This means that we're picking up an OpenSSL // dependency on non-Windows platforms (where it has its own HTTPS // subsystem). As a result, we need to link to OpenSSL. // // Now actually *linking* to OpenSSL isn't so hard. We just need to make // sure to use pkg-config to discover any relevant system dependencies for // differences between distributions like CentOS and Ubuntu. The actual // trickiness comes about when we start *distributing* the resulting // binaries. Currently Cargo is distributed in binary form as nightlies, // which means we're distributing a binary with OpenSSL linked in. // // For historical reasons, the Linux nightly builder is running a CentOS // distribution in order to have as much ABI compatibility with other // distributions as possible. Sadly, however, this compatibility does not // extend to OpenSSL. Currently OpenSSL has two major versions, 0.9 and 1.0, // which are incompatible (many ABI differences). The CentOS builder we // build on has version 1.0, as do most distributions today. Some still have // 0.9, however. This means that if we are to distribute the binaries built // by the CentOS machine, we would only be compatible with OpenSSL 1.0 and // we would fail to run (a dynamic linker error at runtime) on systems with // only 9.8 installed (hopefully). // // But wait, the plot thickens! Apparently CentOS has dubbed their OpenSSL // library as `libssl.so.10`, notably the `10` is included at the end. On // the other hand Ubuntu, for example, only distributes `libssl.so`. This // means that the binaries created at CentOS are hard-wired to probe for a // file called `libssl.so.10` at runtime (using the LD_LIBRARY_PATH), which // will not be found on ubuntu. The conclusion of this is that binaries // built on CentOS cannot be distributed to Ubuntu and run successfully. // // There are a number of sneaky things we could do, including, but not // limited to: // // 1. Create a shim program which runs "just before" cargo runs. The // responsibility of this shim program would be to locate `libssl.so`, // whatever it's called, on the current system, make sure there's a // symlink *somewhere* called `libssl.so.10`, and then set up // LD_LIBRARY_PATH and run the actual cargo. // // This approach definitely seems unconventional, and is borderline // overkill for this problem. It's also dubious if we can find a // libssl.so reliably on the target system. // // 2. Somehow re-work the CentOS installation so that the linked-against // library is called libssl.so instead of libssl.so.10 // // The problem with this approach is that systems with 0.9 installed will // start to silently fail, due to also having libraries called libssl.so // (probably symlinked under a more appropriate version). // // 3. Compile Cargo against both OpenSSL 1.0 *and* OpenSSL 0.9, and // distribute both. Also make sure that the linked-against name of the // library is `libssl.so`. At runtime we determine which version is // installed, and we then the appropriate binary. // // This approach clearly has drawbacks in terms of infrastructure and // feasibility. // // 4. Build a nightly of Cargo for each distribution we'd like to support. // You would then pick the appropriate Cargo nightly to install locally. // // So, with all this in mind, the decision was made to *statically* link // OpenSSL. This solves any problem of relying on a downstream OpenSSL // version being available. This does, however, open a can of worms related // to security issues. It's generally a good idea to dynamically link // OpenSSL as you'll get security updates over time without having to do // anything (the system administrator will update the local openssl // package). By statically linking, we're forfeiting this feature. // // The conclusion was made it is likely appropriate for the Cargo nightlies // to statically link OpenSSL, but highly encourage distributions and // packagers of Cargo to dynamically link OpenSSL. Packagers are targeting // one system and are distributing to only that system, so none of the // problems mentioned above would arise. // // In order to support this, a new package was made: openssl-static-sys. // This package currently performs a fairly simple task: // // 1. Run pkg-config to discover where openssl is installed. // 2. If openssl is installed in a nonstandard location, *and* static copies // of the libraries are available, copy them to $OUT_DIR. // // This library will bring in libssl.a and libcrypto.a into the local build, // allowing them to be picked up by this crate. This allows us to configure // our own buildbots to have pkg-config point to these local pre-built // copies of a static OpenSSL (with very few dependencies) while allowing // most other builds of Cargo to naturally dynamically link OpenSSL. // // So in summary, if you're with me so far, we've statically linked OpenSSL // to the Cargo binary (or any binary, for that matter) and we're ready to // distribute it to *all* linux distributions. Remember that our original // intent for openssl was for HTTPS support, which implies that we need some // for of CA certificate store to validate certificates. This is normally // installed in a standard system location. // // Unfortunately, as one might imagine, OpenSSL is configured for where this // standard location is at *build time*, but it often varies widely // per-system. Consequently, it was discovered that OpenSSL will respect the // SSL_CERT_FILE and SSL_CERT_DIR environment variables in order to assist // in discovering the location of this file (hurray!). // // So, finally getting to the point, this function solely exists to support // our static builds of OpenSSL by probing for the "standard system // location" of certificates and setting relevant environment variable to // point to them. // // Ah, and as a final note, this is only a problem on Linux, not on OS X. On // OS X the OpenSSL binaries are stable enough that we can just rely on // dynamic linkage (plus they have some weird modifications to OpenSSL which // means we wouldn't want to link statically). openssl_probe::init_ssl_cert_env_vars(); } #[cfg(any( windows, target_os = "macos", target_os = "ios", not(feature = "https") ))] fn openssl_env_init() {} unsafe fn opt_bytes<'a, T>(_anchor: &'a T, c: *const libc::c_char) -> Option<&'a [u8]> { if c.is_null() { None } else { Some(CStr::from_ptr(c).to_bytes()) } } fn opt_cstr(o: Option) -> Result, Error> { match o { Some(s) => s.into_c_string().map(Some), None => Ok(None), } } impl ObjectType { /// Convert an object type to its string representation. pub fn str(&self) -> &'static str { unsafe { let ptr = call!(raw::git_object_type2string(*self)) as *const _; let data = CStr::from_ptr(ptr).to_bytes(); str::from_utf8(data).unwrap() } } /// Determine if the given git_object_t is a valid loose object type. pub fn is_loose(&self) -> bool { unsafe { call!(raw::git_object_typeisloose(*self)) == 1 } } /// Convert a raw git_object_t to an ObjectType pub fn from_raw(raw: raw::git_object_t) -> Option { match raw { raw::GIT_OBJECT_ANY => Some(ObjectType::Any), raw::GIT_OBJECT_COMMIT => Some(ObjectType::Commit), raw::GIT_OBJECT_TREE => Some(ObjectType::Tree), raw::GIT_OBJECT_BLOB => Some(ObjectType::Blob), raw::GIT_OBJECT_TAG => Some(ObjectType::Tag), _ => None, } } /// Convert this kind into its raw representation pub fn raw(&self) -> raw::git_object_t { call::convert(self) } /// Convert a string object type representation to its object type. pub fn from_str(s: &str) -> Option { let raw = unsafe { call!(raw::git_object_string2type(CString::new(s).unwrap())) }; ObjectType::from_raw(raw) } } impl fmt::Display for ObjectType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.str().fmt(f) } } impl ReferenceType { /// Convert an object type to its string representation. pub fn str(&self) -> &'static str { match self { ReferenceType::Direct => "direct", ReferenceType::Symbolic => "symbolic", } } /// Convert a raw git_reference_t to a ReferenceType. pub fn from_raw(raw: raw::git_reference_t) -> Option { match raw { raw::GIT_REFERENCE_DIRECT => Some(ReferenceType::Direct), raw::GIT_REFERENCE_SYMBOLIC => Some(ReferenceType::Symbolic), _ => None, } } } impl fmt::Display for ReferenceType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.str().fmt(f) } } impl ConfigLevel { /// Converts a raw configuration level to a ConfigLevel pub fn from_raw(raw: raw::git_config_level_t) -> ConfigLevel { match raw { raw::GIT_CONFIG_LEVEL_PROGRAMDATA => ConfigLevel::ProgramData, raw::GIT_CONFIG_LEVEL_SYSTEM => ConfigLevel::System, 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_APP => ConfigLevel::App, raw::GIT_CONFIG_HIGHEST_LEVEL => ConfigLevel::Highest, n => panic!("unknown config level: {}", n), } } } impl SubmoduleIgnore { /// Converts a [`raw::git_submodule_ignore_t`] to a [`SubmoduleIgnore`] pub fn from_raw(raw: raw::git_submodule_ignore_t) -> Self { match raw { raw::GIT_SUBMODULE_IGNORE_UNSPECIFIED => SubmoduleIgnore::Unspecified, raw::GIT_SUBMODULE_IGNORE_NONE => SubmoduleIgnore::None, raw::GIT_SUBMODULE_IGNORE_UNTRACKED => SubmoduleIgnore::Untracked, raw::GIT_SUBMODULE_IGNORE_DIRTY => SubmoduleIgnore::Dirty, raw::GIT_SUBMODULE_IGNORE_ALL => SubmoduleIgnore::All, n => panic!("unknown submodule ignore rule: {}", n), } } } impl SubmoduleUpdate { /// Converts a [`raw::git_submodule_update_t`] to a [`SubmoduleUpdate`] pub fn from_raw(raw: raw::git_submodule_update_t) -> Self { match raw { raw::GIT_SUBMODULE_UPDATE_CHECKOUT => SubmoduleUpdate::Checkout, raw::GIT_SUBMODULE_UPDATE_REBASE => SubmoduleUpdate::Rebase, raw::GIT_SUBMODULE_UPDATE_MERGE => SubmoduleUpdate::Merge, raw::GIT_SUBMODULE_UPDATE_NONE => SubmoduleUpdate::None, raw::GIT_SUBMODULE_UPDATE_DEFAULT => SubmoduleUpdate::Default, n => panic!("unknown submodule update strategy: {}", n), } } } bitflags! { /// Status flags for a single file /// /// A combination of these values will be returned to indicate the status of /// a file. Status compares the working directory, the index, and the /// current HEAD of the repository. The `STATUS_INDEX_*` set of flags /// represents the status of file in the index relative to the HEAD, and the /// `STATUS_WT_*` set of flags represent the status of the file in the /// working directory relative to the index. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Status: u32 { #[allow(missing_docs)] const CURRENT = raw::GIT_STATUS_CURRENT as u32; #[allow(missing_docs)] const INDEX_NEW = raw::GIT_STATUS_INDEX_NEW as u32; #[allow(missing_docs)] const INDEX_MODIFIED = raw::GIT_STATUS_INDEX_MODIFIED as u32; #[allow(missing_docs)] const INDEX_DELETED = raw::GIT_STATUS_INDEX_DELETED as u32; #[allow(missing_docs)] const INDEX_RENAMED = raw::GIT_STATUS_INDEX_RENAMED as u32; #[allow(missing_docs)] const INDEX_TYPECHANGE = raw::GIT_STATUS_INDEX_TYPECHANGE as u32; #[allow(missing_docs)] const WT_NEW = raw::GIT_STATUS_WT_NEW as u32; #[allow(missing_docs)] const WT_MODIFIED = raw::GIT_STATUS_WT_MODIFIED as u32; #[allow(missing_docs)] const WT_DELETED = raw::GIT_STATUS_WT_DELETED as u32; #[allow(missing_docs)] const WT_TYPECHANGE = raw::GIT_STATUS_WT_TYPECHANGE as u32; #[allow(missing_docs)] const WT_RENAMED = raw::GIT_STATUS_WT_RENAMED as u32; #[allow(missing_docs)] const IGNORED = raw::GIT_STATUS_IGNORED as u32; #[allow(missing_docs)] const CONFLICTED = raw::GIT_STATUS_CONFLICTED as u32; } } impl Status { is_bit_set!(is_index_new, Status::INDEX_NEW); is_bit_set!(is_index_modified, Status::INDEX_MODIFIED); is_bit_set!(is_index_deleted, Status::INDEX_DELETED); is_bit_set!(is_index_renamed, Status::INDEX_RENAMED); is_bit_set!(is_index_typechange, Status::INDEX_TYPECHANGE); is_bit_set!(is_wt_new, Status::WT_NEW); is_bit_set!(is_wt_modified, Status::WT_MODIFIED); is_bit_set!(is_wt_deleted, Status::WT_DELETED); is_bit_set!(is_wt_typechange, Status::WT_TYPECHANGE); is_bit_set!(is_wt_renamed, Status::WT_RENAMED); is_bit_set!(is_ignored, Status::IGNORED); is_bit_set!(is_conflicted, Status::CONFLICTED); } bitflags! { /// Mode options for RepositoryInitOptions #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RepositoryInitMode: u32 { /// Use permissions configured by umask - the default const SHARED_UMASK = raw::GIT_REPOSITORY_INIT_SHARED_UMASK as u32; /// Use `--shared=group` behavior, chmod'ing the new repo to be /// group writable and \"g+sx\" for sticky group assignment const SHARED_GROUP = raw::GIT_REPOSITORY_INIT_SHARED_GROUP as u32; /// Use `--shared=all` behavior, adding world readability. const SHARED_ALL = raw::GIT_REPOSITORY_INIT_SHARED_ALL as u32; } } impl RepositoryInitMode { is_bit_set!(is_shared_umask, RepositoryInitMode::SHARED_UMASK); is_bit_set!(is_shared_group, RepositoryInitMode::SHARED_GROUP); is_bit_set!(is_shared_all, RepositoryInitMode::SHARED_ALL); } /// What type of change is described by a `DiffDelta`? #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Delta { /// No changes Unmodified, /// Entry does not exist in old version Added, /// Entry does not exist in new version Deleted, /// Entry content changed between old and new Modified, /// Entry was renamed between old and new Renamed, /// Entry was copied from another old entry Copied, /// Entry is ignored item in workdir Ignored, /// Entry is untracked item in workdir Untracked, /// Type of entry changed between old and new Typechange, /// Entry is unreadable Unreadable, /// Entry in the index is conflicted Conflicted, } /// Valid modes for index and tree entries. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum FileMode { /// Unreadable Unreadable, /// Tree Tree, /// Blob Blob, /// Group writable blob. Obsolete mode kept for compatibility reasons BlobGroupWritable, /// Blob executable BlobExecutable, /// Link Link, /// Commit Commit, } impl From for i32 { fn from(mode: FileMode) -> i32 { match mode { FileMode::Unreadable => raw::GIT_FILEMODE_UNREADABLE as i32, FileMode::Tree => raw::GIT_FILEMODE_TREE as i32, FileMode::Blob => raw::GIT_FILEMODE_BLOB as i32, FileMode::BlobGroupWritable => raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE as i32, FileMode::BlobExecutable => raw::GIT_FILEMODE_BLOB_EXECUTABLE as i32, FileMode::Link => raw::GIT_FILEMODE_LINK as i32, FileMode::Commit => raw::GIT_FILEMODE_COMMIT as i32, } } } impl From for u32 { fn from(mode: FileMode) -> u32 { match mode { FileMode::Unreadable => raw::GIT_FILEMODE_UNREADABLE as u32, FileMode::Tree => raw::GIT_FILEMODE_TREE as u32, FileMode::Blob => raw::GIT_FILEMODE_BLOB as u32, FileMode::BlobGroupWritable => raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE as u32, FileMode::BlobExecutable => raw::GIT_FILEMODE_BLOB_EXECUTABLE as u32, FileMode::Link => raw::GIT_FILEMODE_LINK as u32, FileMode::Commit => raw::GIT_FILEMODE_COMMIT as u32, } } } bitflags! { /// Return codes for submodule status. /// /// A combination of these flags will be returned to describe the status of a /// submodule. Depending on the "ignore" property of the submodule, some of /// the flags may never be returned because they indicate changes that are /// supposed to be ignored. /// /// Submodule info is contained in 4 places: the HEAD tree, the index, config /// files (both .git/config and .gitmodules), and the working directory. Any /// or all of those places might be missing information about the submodule /// depending on what state the repo is in. We consider all four places to /// build the combination of status flags. /// /// There are four values that are not really status, but give basic info /// about what sources of submodule data are available. These will be /// returned even if ignore is set to "ALL". /// /// * IN_HEAD - superproject head contains submodule /// * IN_INDEX - superproject index contains submodule /// * IN_CONFIG - superproject gitmodules has submodule /// * IN_WD - superproject workdir has submodule /// /// The following values will be returned so long as ignore is not "ALL". /// /// * INDEX_ADDED - in index, not in head /// * INDEX_DELETED - in head, not in index /// * INDEX_MODIFIED - index and head don't match /// * WD_UNINITIALIZED - workdir contains empty directory /// * WD_ADDED - in workdir, not index /// * WD_DELETED - in index, not workdir /// * WD_MODIFIED - index and workdir head don't match /// /// The following can only be returned if ignore is "NONE" or "UNTRACKED". /// /// * WD_INDEX_MODIFIED - submodule workdir index is dirty /// * WD_WD_MODIFIED - submodule workdir has modified files /// /// Lastly, the following will only be returned for ignore "NONE". /// /// * WD_UNTRACKED - workdir contains untracked files #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct SubmoduleStatus: u32 { #[allow(missing_docs)] const IN_HEAD = raw::GIT_SUBMODULE_STATUS_IN_HEAD as u32; #[allow(missing_docs)] const IN_INDEX = raw::GIT_SUBMODULE_STATUS_IN_INDEX as u32; #[allow(missing_docs)] const IN_CONFIG = raw::GIT_SUBMODULE_STATUS_IN_CONFIG as u32; #[allow(missing_docs)] const IN_WD = raw::GIT_SUBMODULE_STATUS_IN_WD as u32; #[allow(missing_docs)] const INDEX_ADDED = raw::GIT_SUBMODULE_STATUS_INDEX_ADDED as u32; #[allow(missing_docs)] const INDEX_DELETED = raw::GIT_SUBMODULE_STATUS_INDEX_DELETED as u32; #[allow(missing_docs)] const INDEX_MODIFIED = raw::GIT_SUBMODULE_STATUS_INDEX_MODIFIED as u32; #[allow(missing_docs)] const WD_UNINITIALIZED = raw::GIT_SUBMODULE_STATUS_WD_UNINITIALIZED as u32; #[allow(missing_docs)] const WD_ADDED = raw::GIT_SUBMODULE_STATUS_WD_ADDED as u32; #[allow(missing_docs)] const WD_DELETED = raw::GIT_SUBMODULE_STATUS_WD_DELETED as u32; #[allow(missing_docs)] const WD_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_MODIFIED as u32; #[allow(missing_docs)] const WD_INDEX_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED as u32; #[allow(missing_docs)] const WD_WD_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_WD_MODIFIED as u32; #[allow(missing_docs)] const WD_UNTRACKED = raw::GIT_SUBMODULE_STATUS_WD_UNTRACKED as u32; } } impl SubmoduleStatus { is_bit_set!(is_in_head, SubmoduleStatus::IN_HEAD); is_bit_set!(is_in_index, SubmoduleStatus::IN_INDEX); is_bit_set!(is_in_config, SubmoduleStatus::IN_CONFIG); is_bit_set!(is_in_wd, SubmoduleStatus::IN_WD); is_bit_set!(is_index_added, SubmoduleStatus::INDEX_ADDED); is_bit_set!(is_index_deleted, SubmoduleStatus::INDEX_DELETED); is_bit_set!(is_index_modified, SubmoduleStatus::INDEX_MODIFIED); is_bit_set!(is_wd_uninitialized, SubmoduleStatus::WD_UNINITIALIZED); is_bit_set!(is_wd_added, SubmoduleStatus::WD_ADDED); is_bit_set!(is_wd_deleted, SubmoduleStatus::WD_DELETED); is_bit_set!(is_wd_modified, SubmoduleStatus::WD_MODIFIED); is_bit_set!(is_wd_wd_modified, SubmoduleStatus::WD_WD_MODIFIED); is_bit_set!(is_wd_untracked, SubmoduleStatus::WD_UNTRACKED); } /// Submodule ignore values /// /// These values represent settings for the `submodule.$name.ignore` /// configuration value which says how deeply to look at the working /// directory when getting the submodule status. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SubmoduleIgnore { /// Use the submodule's configuration Unspecified, /// Any change or untracked file is considered dirty None, /// Only dirty if tracked files have changed Untracked, /// Only dirty if HEAD has moved Dirty, /// Never dirty All, } /// Submodule update values /// /// These values represent settings for the `submodule.$name.update` /// configuration value which says how to handle `git submodule update` /// for this submodule. The value is usually set in the ".gitmodules" /// file and copied to ".git/config" when the submodule is initialized. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SubmoduleUpdate { /// The default; when a submodule is updated, checkout the new detached /// HEAD to the submodule directory. Checkout, /// Update by rebasing the current checked out branch onto the commit from /// the superproject. Rebase, /// Update by merging the commit in the superproject into the current /// checkout out branch of the submodule. Merge, /// Do not update this submodule even when the commit in the superproject /// is updated. None, /// Not used except as static initializer when we don't want any particular /// update rule to be specified. Default, } bitflags! { /// ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct PathspecFlags: u32 { /// Use the default pathspec matching configuration. const DEFAULT = raw::GIT_PATHSPEC_DEFAULT as u32; /// Force matching to ignore case, otherwise matching will use native /// case sensitivity of the platform filesystem. const IGNORE_CASE = raw::GIT_PATHSPEC_IGNORE_CASE as u32; /// Force case sensitive matches, otherwise match will use the native /// case sensitivity of the platform filesystem. const USE_CASE = raw::GIT_PATHSPEC_USE_CASE as u32; /// Disable glob patterns and just use simple string comparison for /// matching. const NO_GLOB = raw::GIT_PATHSPEC_NO_GLOB as u32; /// Means that match functions return the error code `NotFound` if no /// matches are found. By default no matches is a success. const NO_MATCH_ERROR = raw::GIT_PATHSPEC_NO_MATCH_ERROR as u32; /// Means that the list returned should track which patterns matched /// which files so that at the end of the match we can identify patterns /// that did not match any files. const FIND_FAILURES = raw::GIT_PATHSPEC_FIND_FAILURES as u32; /// Means that the list returned does not need to keep the actual /// matching filenames. Use this to just test if there were any matches /// at all or in combination with `PATHSPEC_FAILURES` to validate a /// pathspec. const FAILURES_ONLY = raw::GIT_PATHSPEC_FAILURES_ONLY as u32; } } impl PathspecFlags { is_bit_set!(is_default, PathspecFlags::DEFAULT); is_bit_set!(is_ignore_case, PathspecFlags::IGNORE_CASE); is_bit_set!(is_use_case, PathspecFlags::USE_CASE); is_bit_set!(is_no_glob, PathspecFlags::NO_GLOB); is_bit_set!(is_no_match_error, PathspecFlags::NO_MATCH_ERROR); is_bit_set!(is_find_failures, PathspecFlags::FIND_FAILURES); is_bit_set!(is_failures_only, PathspecFlags::FAILURES_ONLY); } impl Default for PathspecFlags { fn default() -> Self { PathspecFlags::DEFAULT } } bitflags! { /// Types of notifications emitted from checkouts. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct CheckoutNotificationType: u32 { /// Notification about a conflict. const CONFLICT = raw::GIT_CHECKOUT_NOTIFY_CONFLICT as u32; /// Notification about a dirty file. const DIRTY = raw::GIT_CHECKOUT_NOTIFY_DIRTY as u32; /// Notification about an updated file. const UPDATED = raw::GIT_CHECKOUT_NOTIFY_UPDATED as u32; /// Notification about an untracked file. const UNTRACKED = raw::GIT_CHECKOUT_NOTIFY_UNTRACKED as u32; /// Notification about an ignored file. const IGNORED = raw::GIT_CHECKOUT_NOTIFY_IGNORED as u32; } } impl CheckoutNotificationType { is_bit_set!(is_conflict, CheckoutNotificationType::CONFLICT); is_bit_set!(is_dirty, CheckoutNotificationType::DIRTY); is_bit_set!(is_updated, CheckoutNotificationType::UPDATED); is_bit_set!(is_untracked, CheckoutNotificationType::UNTRACKED); is_bit_set!(is_ignored, CheckoutNotificationType::IGNORED); } /// Possible output formats for diff data #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DiffFormat { /// full git diff Patch, /// just the headers of the patch PatchHeader, /// like git diff --raw Raw, /// like git diff --name-only NameOnly, /// like git diff --name-status NameStatus, /// git diff as used by git patch-id PatchId, } bitflags! { /// Formatting options for diff stats #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct DiffStatsFormat: raw::git_diff_stats_format_t { /// Don't generate any stats const NONE = raw::GIT_DIFF_STATS_NONE; /// Equivalent of `--stat` in git const FULL = raw::GIT_DIFF_STATS_FULL; /// Equivalent of `--shortstat` in git const SHORT = raw::GIT_DIFF_STATS_SHORT; /// Equivalent of `--numstat` in git const NUMBER = raw::GIT_DIFF_STATS_NUMBER; /// Extended header information such as creations, renames and mode /// changes, equivalent of `--summary` in git const INCLUDE_SUMMARY = raw::GIT_DIFF_STATS_INCLUDE_SUMMARY; } } impl DiffStatsFormat { is_bit_set!(is_none, DiffStatsFormat::NONE); is_bit_set!(is_full, DiffStatsFormat::FULL); is_bit_set!(is_short, DiffStatsFormat::SHORT); is_bit_set!(is_number, DiffStatsFormat::NUMBER); is_bit_set!(is_include_summary, DiffStatsFormat::INCLUDE_SUMMARY); } /// Automatic tag following options. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AutotagOption { /// Use the setting from the remote's configuration Unspecified, /// Ask the server for tags pointing to objects we're already downloading Auto, /// Don't ask for any tags beyond the refspecs None, /// Ask for all the tags All, } /// Configuration for how pruning is done on a fetch #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum FetchPrune { /// Use the setting from the configuration Unspecified, /// Force pruning on On, /// Force pruning off Off, } #[allow(missing_docs)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum StashApplyProgress { /// None None, /// Loading the stashed data from the object database LoadingStash, /// The stored index is being analyzed AnalyzeIndex, /// The modified files are being analyzed AnalyzeModified, /// The untracked and ignored files are being analyzed AnalyzeUntracked, /// The untracked files are being written to disk CheckoutUntracked, /// The modified files are being written to disk CheckoutModified, /// The stash was applied successfully Done, } bitflags! { #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct StashApplyFlags: u32 { #[allow(missing_docs)] const DEFAULT = raw::GIT_STASH_APPLY_DEFAULT as u32; /// Try to reinstate not only the working tree's changes, /// but also the index's changes. const REINSTATE_INDEX = raw::GIT_STASH_APPLY_REINSTATE_INDEX as u32; } } impl StashApplyFlags { is_bit_set!(is_default, StashApplyFlags::DEFAULT); is_bit_set!(is_reinstate_index, StashApplyFlags::REINSTATE_INDEX); } impl Default for StashApplyFlags { fn default() -> Self { StashApplyFlags::DEFAULT } } bitflags! { #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct StashFlags: u32 { #[allow(missing_docs)] const DEFAULT = raw::GIT_STASH_DEFAULT as u32; /// All changes already added to the index are left intact in /// the working directory const KEEP_INDEX = raw::GIT_STASH_KEEP_INDEX as u32; /// All untracked files are also stashed and then cleaned up /// from the working directory const INCLUDE_UNTRACKED = raw::GIT_STASH_INCLUDE_UNTRACKED as u32; /// All ignored files are also stashed and then cleaned up from /// the working directory const INCLUDE_IGNORED = raw::GIT_STASH_INCLUDE_IGNORED as u32; /// All changes in the index and working directory are left intact const KEEP_ALL = raw::GIT_STASH_KEEP_ALL as u32; } } impl StashFlags { is_bit_set!(is_default, StashFlags::DEFAULT); is_bit_set!(is_keep_index, StashFlags::KEEP_INDEX); is_bit_set!(is_include_untracked, StashFlags::INCLUDE_UNTRACKED); is_bit_set!(is_include_ignored, StashFlags::INCLUDE_IGNORED); } impl Default for StashFlags { fn default() -> Self { StashFlags::DEFAULT } } bitflags! { #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct AttrCheckFlags: u32 { /// Check the working directory, then the index. const FILE_THEN_INDEX = raw::GIT_ATTR_CHECK_FILE_THEN_INDEX as u32; /// Check the index, then the working directory. const INDEX_THEN_FILE = raw::GIT_ATTR_CHECK_INDEX_THEN_FILE as u32; /// Check the index only. const INDEX_ONLY = raw::GIT_ATTR_CHECK_INDEX_ONLY as u32; /// Do not use the system gitattributes file. const NO_SYSTEM = raw::GIT_ATTR_CHECK_NO_SYSTEM as u32; } } impl Default for AttrCheckFlags { fn default() -> Self { AttrCheckFlags::FILE_THEN_INDEX } } bitflags! { #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct DiffFlags: u32 { /// File(s) treated as binary data. const BINARY = raw::GIT_DIFF_FLAG_BINARY as u32; /// File(s) treated as text data. const NOT_BINARY = raw::GIT_DIFF_FLAG_NOT_BINARY as u32; /// `id` value is known correct. const VALID_ID = raw::GIT_DIFF_FLAG_VALID_ID as u32; /// File exists at this side of the delta. const EXISTS = raw::GIT_DIFF_FLAG_EXISTS as u32; } } impl DiffFlags { is_bit_set!(is_binary, DiffFlags::BINARY); is_bit_set!(is_not_binary, DiffFlags::NOT_BINARY); is_bit_set!(has_valid_id, DiffFlags::VALID_ID); is_bit_set!(exists, DiffFlags::EXISTS); } bitflags! { /// Options for [`Reference::normalize_name`]. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ReferenceFormat: u32 { /// No particular normalization. const NORMAL = raw::GIT_REFERENCE_FORMAT_NORMAL as u32; /// Control whether one-level refname are accepted (i.e., refnames that /// do not contain multiple `/`-separated components). Those are /// expected to be written only using uppercase letters and underscore /// (e.g. `HEAD`, `FETCH_HEAD`). const ALLOW_ONELEVEL = raw::GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL as u32; /// Interpret the provided name as a reference pattern for a refspec (as /// used with remote repositories). If this option is enabled, the name /// is allowed to contain a single `*` in place of a full pathname /// components (e.g., `foo/*/bar` but not `foo/bar*`). const REFSPEC_PATTERN = raw::GIT_REFERENCE_FORMAT_REFSPEC_PATTERN as u32; /// Interpret the name as part of a refspec in shorthand form so the /// `ALLOW_ONELEVEL` naming rules aren't enforced and `main` becomes a /// valid name. const REFSPEC_SHORTHAND = raw::GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND as u32; } } impl ReferenceFormat { is_bit_set!(is_allow_onelevel, ReferenceFormat::ALLOW_ONELEVEL); is_bit_set!(is_refspec_pattern, ReferenceFormat::REFSPEC_PATTERN); is_bit_set!(is_refspec_shorthand, ReferenceFormat::REFSPEC_SHORTHAND); } impl Default for ReferenceFormat { fn default() -> Self { ReferenceFormat::NORMAL } } #[cfg(test)] mod tests { use super::{FileMode, ObjectType}; #[test] fn convert() { assert_eq!(ObjectType::Blob.str(), "blob"); assert_eq!(ObjectType::from_str("blob"), Some(ObjectType::Blob)); assert!(ObjectType::Blob.is_loose()); } #[test] fn convert_filemode() { assert_eq!(i32::from(FileMode::Blob), 0o100644); assert_eq!(i32::from(FileMode::BlobGroupWritable), 0o100664); assert_eq!(i32::from(FileMode::BlobExecutable), 0o100755); assert_eq!(u32::from(FileMode::Blob), 0o100644); assert_eq!(u32::from(FileMode::BlobGroupWritable), 0o100664); assert_eq!(u32::from(FileMode::BlobExecutable), 0o100755); } #[test] fn bitflags_partial_eq() { use super::{ AttrCheckFlags, CheckoutNotificationType, CredentialType, DiffFlags, DiffStatsFormat, IndexAddOption, IndexEntryExtendedFlag, IndexEntryFlag, MergeAnalysis, MergePreference, OdbLookupFlags, PathspecFlags, ReferenceFormat, RepositoryInitMode, RepositoryOpenFlags, RevparseMode, Sort, StashApplyFlags, StashFlags, Status, SubmoduleStatus, }; assert_eq!( AttrCheckFlags::FILE_THEN_INDEX, AttrCheckFlags::FILE_THEN_INDEX ); assert_eq!( CheckoutNotificationType::CONFLICT, CheckoutNotificationType::CONFLICT ); assert_eq!( CredentialType::USER_PASS_PLAINTEXT, CredentialType::USER_PASS_PLAINTEXT ); assert_eq!(DiffFlags::BINARY, DiffFlags::BINARY); assert_eq!( DiffStatsFormat::INCLUDE_SUMMARY, DiffStatsFormat::INCLUDE_SUMMARY ); assert_eq!( IndexAddOption::CHECK_PATHSPEC, IndexAddOption::CHECK_PATHSPEC ); assert_eq!( IndexEntryExtendedFlag::INTENT_TO_ADD, IndexEntryExtendedFlag::INTENT_TO_ADD ); assert_eq!(IndexEntryFlag::EXTENDED, IndexEntryFlag::EXTENDED); assert_eq!( MergeAnalysis::ANALYSIS_FASTFORWARD, MergeAnalysis::ANALYSIS_FASTFORWARD ); assert_eq!( MergePreference::FASTFORWARD_ONLY, MergePreference::FASTFORWARD_ONLY ); assert_eq!(OdbLookupFlags::NO_REFRESH, OdbLookupFlags::NO_REFRESH); assert_eq!(PathspecFlags::FAILURES_ONLY, PathspecFlags::FAILURES_ONLY); assert_eq!( ReferenceFormat::ALLOW_ONELEVEL, ReferenceFormat::ALLOW_ONELEVEL ); assert_eq!( RepositoryInitMode::SHARED_ALL, RepositoryInitMode::SHARED_ALL ); assert_eq!(RepositoryOpenFlags::CROSS_FS, RepositoryOpenFlags::CROSS_FS); assert_eq!(RevparseMode::RANGE, RevparseMode::RANGE); assert_eq!(Sort::REVERSE, Sort::REVERSE); assert_eq!( StashApplyFlags::REINSTATE_INDEX, StashApplyFlags::REINSTATE_INDEX ); assert_eq!(StashFlags::INCLUDE_IGNORED, StashFlags::INCLUDE_IGNORED); assert_eq!(Status::WT_MODIFIED, Status::WT_MODIFIED); assert_eq!(SubmoduleStatus::WD_ADDED, SubmoduleStatus::WD_ADDED); } }