git2/
lib.rs

1//! # libgit2 bindings for Rust
2//!
3//! This library contains bindings to the [libgit2][1] C library which is used
4//! to manage git repositories. The library itself is a work in progress and is
5//! likely lacking some bindings here and there, so be warned.
6//!
7//! [1]: https://libgit2.github.com/
8//!
9//! The git2-rs library strives to be as close to libgit2 as possible, but also
10//! strives to make using libgit2 as safe as possible. All resource management
11//! is automatic as well as adding strong types to all interfaces (including
12//! `Result`)
13//!
14//! ## Creating a `Repository`
15//!
16//! The `Repository` is the source from which almost all other objects in git-rs
17//! are spawned. A repository can be created through opening, initializing, or
18//! cloning.
19//!
20//! ### Initializing a new repository
21//!
22//! The `init` method will create a new repository, assuming one does not
23//! already exist.
24//!
25//! ```no_run
26//! # #![allow(unstable)]
27//! use git2::Repository;
28//!
29//! let repo = match Repository::init("/path/to/a/repo") {
30//!     Ok(repo) => repo,
31//!     Err(e) => panic!("failed to init: {}", e),
32//! };
33//! ```
34//!
35//! ### Opening an existing repository
36//!
37//! ```no_run
38//! # #![allow(unstable)]
39//! use git2::Repository;
40//!
41//! let repo = match Repository::open("/path/to/a/repo") {
42//!     Ok(repo) => repo,
43//!     Err(e) => panic!("failed to open: {}", e),
44//! };
45//! ```
46//!
47//! ### Cloning an existing repository
48//!
49//! ```no_run
50//! # #![allow(unstable)]
51//! use git2::Repository;
52//!
53//! let url = "https://github.com/alexcrichton/git2-rs";
54//! let repo = match Repository::clone(url, "/path/to/a/repo") {
55//!     Ok(repo) => repo,
56//!     Err(e) => panic!("failed to clone: {}", e),
57//! };
58//! ```
59//!
60//! To clone using SSH, refer to [RepoBuilder](./build/struct.RepoBuilder.html).
61//!
62//! ## Working with a `Repository`
63//!
64//! All derivative objects, references, etc are attached to the lifetime of the
65//! source `Repository`, to ensure that they do not outlive the repository
66//! itself.
67
68#![doc(html_root_url = "https://docs.rs/git2/0.20")]
69#![allow(trivial_numeric_casts, trivial_casts)]
70#![deny(missing_docs)]
71#![warn(rust_2018_idioms)]
72#![cfg_attr(test, deny(warnings))]
73
74use bitflags::bitflags;
75use libgit2_sys as raw;
76
77use std::ffi::{CStr, CString};
78use std::fmt;
79use std::str;
80use std::sync::Once;
81
82pub use crate::apply::{ApplyLocation, ApplyOptions};
83pub use crate::attr::AttrValue;
84pub use crate::blame::{Blame, BlameHunk, BlameIter, BlameOptions};
85pub use crate::blob::{Blob, BlobWriter};
86pub use crate::branch::{Branch, Branches};
87pub use crate::buf::Buf;
88pub use crate::cherrypick::CherrypickOptions;
89pub use crate::commit::{Commit, Parents};
90pub use crate::config::{Config, ConfigEntries, ConfigEntry};
91pub use crate::cred::{Cred, CredentialHelper};
92pub use crate::describe::{Describe, DescribeFormatOptions, DescribeOptions};
93pub use crate::diff::{Deltas, Diff, DiffDelta, DiffFile, DiffOptions};
94pub use crate::diff::{DiffBinary, DiffBinaryFile, DiffBinaryKind, DiffPatchidOptions};
95pub use crate::diff::{DiffFindOptions, DiffHunk, DiffLine, DiffLineType, DiffStats};
96pub use crate::email::{Email, EmailCreateOptions};
97pub use crate::error::Error;
98pub use crate::index::{
99    Index, IndexConflict, IndexConflicts, IndexEntries, IndexEntry, IndexMatchedPath,
100};
101pub use crate::indexer::{Indexer, IndexerProgress, Progress};
102pub use crate::mailmap::Mailmap;
103pub use crate::mempack::Mempack;
104pub use crate::merge::{AnnotatedCommit, MergeOptions};
105pub use crate::message::{
106    message_prettify, message_trailers_bytes, message_trailers_strs, MessageTrailersBytes,
107    MessageTrailersBytesIterator, MessageTrailersStrs, MessageTrailersStrsIterator,
108    DEFAULT_COMMENT_CHAR,
109};
110pub use crate::note::{Note, Notes};
111pub use crate::object::Object;
112pub use crate::odb::{Odb, OdbObject, OdbPackwriter, OdbReader, OdbWriter};
113pub use crate::oid::Oid;
114pub use crate::packbuilder::{PackBuilder, PackBuilderStage};
115pub use crate::patch::Patch;
116pub use crate::pathspec::{Pathspec, PathspecFailedEntries, PathspecMatchList};
117pub use crate::pathspec::{PathspecDiffEntries, PathspecEntries};
118pub use crate::proxy_options::ProxyOptions;
119pub use crate::push_update::PushUpdate;
120pub use crate::rebase::{Rebase, RebaseOperation, RebaseOperationType, RebaseOptions};
121pub use crate::reference::{Reference, ReferenceNames, References};
122pub use crate::reflog::{Reflog, ReflogEntry, ReflogIter};
123pub use crate::refspec::Refspec;
124pub use crate::remote::{
125    FetchOptions, PushOptions, Refspecs, Remote, RemoteConnection, RemoteHead, RemoteRedirect,
126};
127pub use crate::remote_callbacks::{CertificateCheckStatus, Credentials, RemoteCallbacks};
128pub use crate::remote_callbacks::{TransportMessage, UpdateTips};
129pub use crate::repo::{Repository, RepositoryInitOptions};
130pub use crate::revert::RevertOptions;
131pub use crate::revspec::Revspec;
132pub use crate::revwalk::Revwalk;
133pub use crate::signature::Signature;
134pub use crate::stash::{StashApplyOptions, StashApplyProgressCb, StashCb, StashSaveOptions};
135pub use crate::status::{StatusEntry, StatusIter, StatusOptions, StatusShow, Statuses};
136pub use crate::submodule::{Submodule, SubmoduleUpdateOptions};
137pub use crate::tag::Tag;
138pub use crate::time::{IndexTime, Time};
139pub use crate::tracing::{trace_set, TraceLevel};
140pub use crate::transaction::Transaction;
141pub use crate::tree::{Tree, TreeEntry, TreeIter, TreeWalkMode, TreeWalkResult};
142pub use crate::treebuilder::TreeBuilder;
143pub use crate::util::IntoCString;
144pub use crate::version::Version;
145pub use crate::worktree::{Worktree, WorktreeAddOptions, WorktreeLockStatus, WorktreePruneOptions};
146
147// Create a convinience method on bitflag struct which checks the given flag
148macro_rules! is_bit_set {
149    ($name:ident, $flag:expr) => {
150        #[allow(missing_docs)]
151        pub fn $name(&self) -> bool {
152            self.intersects($flag)
153        }
154    };
155}
156
157/// An enumeration of possible errors that can happen when working with a git
158/// repository.
159// Note: We omit a few native error codes, as they are unlikely to be propagated
160// to the library user. Currently:
161//
162// * GIT_EPASSTHROUGH
163// * GIT_ITEROVER
164// * GIT_RETRY
165#[derive(PartialEq, Eq, Clone, Debug, Copy)]
166pub enum ErrorCode {
167    /// Generic error
168    GenericError,
169    /// Requested object could not be found
170    NotFound,
171    /// Object exists preventing operation
172    Exists,
173    /// More than one object matches
174    Ambiguous,
175    /// Output buffer too short to hold data
176    BufSize,
177    /// User-generated error
178    User,
179    /// Operation not allowed on bare repository
180    BareRepo,
181    /// HEAD refers to branch with no commits
182    UnbornBranch,
183    /// Merge in progress prevented operation
184    Unmerged,
185    /// Reference was not fast-forwardable
186    NotFastForward,
187    /// Name/ref spec was not in a valid format
188    InvalidSpec,
189    /// Checkout conflicts prevented operation
190    Conflict,
191    /// Lock file prevented operation
192    Locked,
193    /// Reference value does not match expected
194    Modified,
195    /// Authentication error
196    Auth,
197    /// Server certificate is invalid
198    Certificate,
199    /// Patch/merge has already been applied
200    Applied,
201    /// The requested peel operation is not possible
202    Peel,
203    /// Unexpected EOF
204    Eof,
205    /// Invalid operation or input
206    Invalid,
207    /// Uncommitted changes in index prevented operation
208    Uncommitted,
209    /// Operation was not valid for a directory
210    Directory,
211    /// A merge conflict exists and cannot continue
212    MergeConflict,
213    /// Hashsum mismatch in object
214    HashsumMismatch,
215    /// Unsaved changes in the index would be overwritten
216    IndexDirty,
217    /// Patch application failed
218    ApplyFail,
219    /// The object is not owned by the current user
220    Owner,
221    /// Timeout
222    Timeout,
223}
224
225/// An enumeration of possible categories of things that can have
226/// errors when working with a git repository.
227#[derive(PartialEq, Eq, Clone, Debug, Copy)]
228pub enum ErrorClass {
229    /// Uncategorized
230    None,
231    /// Out of memory or insufficient allocated space
232    NoMemory,
233    /// Syscall or standard system library error
234    Os,
235    /// Invalid input
236    Invalid,
237    /// Error resolving or manipulating a reference
238    Reference,
239    /// ZLib failure
240    Zlib,
241    /// Bad repository state
242    Repository,
243    /// Bad configuration
244    Config,
245    /// Regex failure
246    Regex,
247    /// Bad object
248    Odb,
249    /// Invalid index data
250    Index,
251    /// Error creating or obtaining an object
252    Object,
253    /// Network error
254    Net,
255    /// Error manipulating a tag
256    Tag,
257    /// Invalid value in tree
258    Tree,
259    /// Hashing or packing error
260    Indexer,
261    /// Error from SSL
262    Ssl,
263    /// Error involving submodules
264    Submodule,
265    /// Threading error
266    Thread,
267    /// Error manipulating a stash
268    Stash,
269    /// Checkout failure
270    Checkout,
271    /// Invalid FETCH_HEAD
272    FetchHead,
273    /// Merge failure
274    Merge,
275    /// SSH failure
276    Ssh,
277    /// Error manipulating filters
278    Filter,
279    /// Error reverting commit
280    Revert,
281    /// Error from a user callback
282    Callback,
283    /// Error cherry-picking commit
284    CherryPick,
285    /// Can't describe object
286    Describe,
287    /// Error during rebase
288    Rebase,
289    /// Filesystem-related error
290    Filesystem,
291    /// Invalid patch data
292    Patch,
293    /// Error involving worktrees
294    Worktree,
295    /// Hash library error or SHA-1 collision
296    Sha1,
297    /// HTTP error
298    Http,
299}
300
301/// A listing of the possible states that a repository can be in.
302#[derive(PartialEq, Eq, Clone, Debug, Copy)]
303#[allow(missing_docs)]
304pub enum RepositoryState {
305    Clean,
306    Merge,
307    Revert,
308    RevertSequence,
309    CherryPick,
310    CherryPickSequence,
311    Bisect,
312    Rebase,
313    RebaseInteractive,
314    RebaseMerge,
315    ApplyMailbox,
316    ApplyMailboxOrRebase,
317}
318
319/// An enumeration of the possible directions for a remote.
320#[derive(Copy, Clone, Debug, PartialEq, Eq)]
321pub enum Direction {
322    /// Data will be fetched (read) from this remote.
323    Fetch,
324    /// Data will be pushed (written) to this remote.
325    Push,
326}
327
328/// An enumeration of the operations that can be performed for the `reset`
329/// method on a `Repository`.
330#[derive(Copy, Clone, Debug, PartialEq, Eq)]
331pub enum ResetType {
332    /// Move the head to the given commit.
333    Soft,
334    /// Soft plus reset the index to the commit.
335    Mixed,
336    /// Mixed plus changes in the working tree are discarded.
337    Hard,
338}
339
340/// An enumeration all possible kinds objects may have.
341#[derive(PartialEq, Eq, Copy, Clone, Debug)]
342pub enum ObjectType {
343    /// Any kind of git object
344    Any,
345    /// An object which corresponds to a git commit
346    Commit,
347    /// An object which corresponds to a git tree
348    Tree,
349    /// An object which corresponds to a git blob
350    Blob,
351    /// An object which corresponds to a git tag
352    Tag,
353}
354
355/// An enumeration of all possible kinds of references.
356#[derive(PartialEq, Eq, Copy, Clone, Debug)]
357pub enum ReferenceType {
358    /// A reference which points at an object id.
359    Direct,
360
361    /// A reference which points at another reference.
362    Symbolic,
363}
364
365/// An enumeration for the possible types of branches
366#[derive(PartialEq, Eq, Debug, Copy, Clone)]
367pub enum BranchType {
368    /// A local branch not on a remote.
369    Local,
370    /// A branch for a remote.
371    Remote,
372}
373
374/// An enumeration of the possible priority levels of a config file.
375///
376/// The levels corresponding to the escalation logic (higher to lower) when
377/// searching for config entries.
378#[derive(PartialEq, Eq, Debug, Copy, Clone)]
379pub enum ConfigLevel {
380    /// System-wide on Windows, for compatibility with portable git
381    ProgramData = 1,
382    /// System-wide configuration file, e.g. /etc/gitconfig
383    System,
384    /// XDG-compatible configuration file, e.g. ~/.config/git/config
385    XDG,
386    /// User-specific configuration, e.g. ~/.gitconfig
387    Global,
388    /// Repository specific config, e.g. $PWD/.git/config
389    Local,
390    ///  Worktree specific configuration file, e.g. $GIT_DIR/config.worktree
391    Worktree,
392    /// Application specific configuration file
393    App,
394    /// Highest level available
395    Highest = -1,
396}
397
398/// Merge file favor options for `MergeOptions` instruct the file-level
399/// merging functionality how to deal with conflicting regions of the files.
400#[derive(PartialEq, Eq, Debug, Copy, Clone)]
401pub enum FileFavor {
402    /// When a region of a file is changed in both branches, a conflict will be
403    /// recorded in the index so that git_checkout can produce a merge file with
404    /// conflict markers in the working directory. This is the default.
405    Normal,
406    /// When a region of a file is changed in both branches, the file created
407    /// in the index will contain the "ours" side of any conflicting region.
408    /// The index will not record a conflict.
409    Ours,
410    /// When a region of a file is changed in both branches, the file created
411    /// in the index will contain the "theirs" side of any conflicting region.
412    /// The index will not record a conflict.
413    Theirs,
414    /// When a region of a file is changed in both branches, the file created
415    /// in the index will contain each unique line from each side, which has
416    /// the result of combining both files. The index will not record a conflict.
417    Union,
418}
419
420bitflags! {
421    /// Orderings that may be specified for Revwalk iteration.
422    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
423    pub struct Sort: u32 {
424        /// Sort the repository contents in no particular ordering.
425        ///
426        /// This sorting is arbitrary, implementation-specific, and subject to
427        /// change at any time. This is the default sorting for new walkers.
428        const NONE = raw::GIT_SORT_NONE as u32;
429
430        /// Sort the repository contents in topological order (children before
431        /// parents).
432        ///
433        /// This sorting mode can be combined with time sorting.
434        const TOPOLOGICAL = raw::GIT_SORT_TOPOLOGICAL as u32;
435
436        /// Sort the repository contents by commit time.
437        ///
438        /// This sorting mode can be combined with topological sorting.
439        const TIME = raw::GIT_SORT_TIME as u32;
440
441        /// Iterate through the repository contents in reverse order.
442        ///
443        /// This sorting mode can be combined with any others.
444        const REVERSE = raw::GIT_SORT_REVERSE as u32;
445    }
446}
447
448impl Sort {
449    is_bit_set!(is_none, Sort::NONE);
450    is_bit_set!(is_topological, Sort::TOPOLOGICAL);
451    is_bit_set!(is_time, Sort::TIME);
452    is_bit_set!(is_reverse, Sort::REVERSE);
453}
454
455bitflags! {
456    /// Types of credentials that can be requested by a credential callback.
457    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
458    pub struct CredentialType: u32 {
459        #[allow(missing_docs)]
460        const USER_PASS_PLAINTEXT = raw::GIT_CREDTYPE_USERPASS_PLAINTEXT as u32;
461        #[allow(missing_docs)]
462        const SSH_KEY = raw::GIT_CREDTYPE_SSH_KEY as u32;
463        #[allow(missing_docs)]
464        const SSH_MEMORY = raw::GIT_CREDTYPE_SSH_MEMORY as u32;
465        #[allow(missing_docs)]
466        const SSH_CUSTOM = raw::GIT_CREDTYPE_SSH_CUSTOM as u32;
467        #[allow(missing_docs)]
468        const DEFAULT = raw::GIT_CREDTYPE_DEFAULT as u32;
469        #[allow(missing_docs)]
470        const SSH_INTERACTIVE = raw::GIT_CREDTYPE_SSH_INTERACTIVE as u32;
471        #[allow(missing_docs)]
472        const USERNAME = raw::GIT_CREDTYPE_USERNAME as u32;
473    }
474}
475
476impl CredentialType {
477    is_bit_set!(is_user_pass_plaintext, CredentialType::USER_PASS_PLAINTEXT);
478    is_bit_set!(is_ssh_key, CredentialType::SSH_KEY);
479    is_bit_set!(is_ssh_memory, CredentialType::SSH_MEMORY);
480    is_bit_set!(is_ssh_custom, CredentialType::SSH_CUSTOM);
481    is_bit_set!(is_default, CredentialType::DEFAULT);
482    is_bit_set!(is_ssh_interactive, CredentialType::SSH_INTERACTIVE);
483    is_bit_set!(is_username, CredentialType::USERNAME);
484}
485
486impl Default for CredentialType {
487    fn default() -> Self {
488        CredentialType::DEFAULT
489    }
490}
491
492bitflags! {
493    /// Flags for the `flags` field of an IndexEntry.
494    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
495    pub struct IndexEntryFlag: u16 {
496        /// Set when the `extended_flags` field is valid.
497        const EXTENDED = raw::GIT_INDEX_ENTRY_EXTENDED as u16;
498        /// "Assume valid" flag
499        const VALID = raw::GIT_INDEX_ENTRY_VALID as u16;
500    }
501}
502
503impl IndexEntryFlag {
504    is_bit_set!(is_extended, IndexEntryFlag::EXTENDED);
505    is_bit_set!(is_valid, IndexEntryFlag::VALID);
506}
507
508bitflags! {
509    /// Flags for the `extended_flags` field of an IndexEntry.
510    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
511    pub struct IndexEntryExtendedFlag: u16 {
512        /// An "intent to add" entry from "git add -N"
513        const INTENT_TO_ADD = raw::GIT_INDEX_ENTRY_INTENT_TO_ADD as u16;
514        /// Skip the associated worktree file, for sparse checkouts
515        const SKIP_WORKTREE = raw::GIT_INDEX_ENTRY_SKIP_WORKTREE as u16;
516
517        #[allow(missing_docs)]
518        const UPTODATE = raw::GIT_INDEX_ENTRY_UPTODATE as u16;
519    }
520}
521
522impl IndexEntryExtendedFlag {
523    is_bit_set!(is_intent_to_add, IndexEntryExtendedFlag::INTENT_TO_ADD);
524    is_bit_set!(is_skip_worktree, IndexEntryExtendedFlag::SKIP_WORKTREE);
525    is_bit_set!(is_up_to_date, IndexEntryExtendedFlag::UPTODATE);
526}
527
528bitflags! {
529    /// Flags for APIs that add files matching pathspec
530    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
531    pub struct IndexAddOption: u32 {
532        #[allow(missing_docs)]
533        const DEFAULT = raw::GIT_INDEX_ADD_DEFAULT as u32;
534        #[allow(missing_docs)]
535        const FORCE = raw::GIT_INDEX_ADD_FORCE as u32;
536        #[allow(missing_docs)]
537        const DISABLE_PATHSPEC_MATCH =
538                raw::GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH as u32;
539        #[allow(missing_docs)]
540        const CHECK_PATHSPEC = raw::GIT_INDEX_ADD_CHECK_PATHSPEC as u32;
541    }
542}
543
544impl IndexAddOption {
545    is_bit_set!(is_default, IndexAddOption::DEFAULT);
546    is_bit_set!(is_force, IndexAddOption::FORCE);
547    is_bit_set!(
548        is_disable_pathspec_match,
549        IndexAddOption::DISABLE_PATHSPEC_MATCH
550    );
551    is_bit_set!(is_check_pathspec, IndexAddOption::CHECK_PATHSPEC);
552}
553
554impl Default for IndexAddOption {
555    fn default() -> Self {
556        IndexAddOption::DEFAULT
557    }
558}
559
560bitflags! {
561    /// Flags for `Repository::open_ext`
562    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
563    pub struct RepositoryOpenFlags: u32 {
564        /// Only open the specified path; don't walk upward searching.
565        const NO_SEARCH = raw::GIT_REPOSITORY_OPEN_NO_SEARCH as u32;
566        /// Search across filesystem boundaries.
567        const CROSS_FS = raw::GIT_REPOSITORY_OPEN_CROSS_FS as u32;
568        /// Force opening as bare repository, and defer loading its config.
569        const BARE = raw::GIT_REPOSITORY_OPEN_BARE as u32;
570        /// Don't try appending `/.git` to the specified repository path.
571        const NO_DOTGIT = raw::GIT_REPOSITORY_OPEN_NO_DOTGIT as u32;
572        /// Respect environment variables like `$GIT_DIR`.
573        const FROM_ENV = raw::GIT_REPOSITORY_OPEN_FROM_ENV as u32;
574    }
575}
576
577impl RepositoryOpenFlags {
578    is_bit_set!(is_no_search, RepositoryOpenFlags::NO_SEARCH);
579    is_bit_set!(is_cross_fs, RepositoryOpenFlags::CROSS_FS);
580    is_bit_set!(is_bare, RepositoryOpenFlags::BARE);
581    is_bit_set!(is_no_dotgit, RepositoryOpenFlags::NO_DOTGIT);
582    is_bit_set!(is_from_env, RepositoryOpenFlags::FROM_ENV);
583}
584
585bitflags! {
586    /// Flags for the return value of `Repository::revparse`
587    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
588    pub struct RevparseMode: u32 {
589        /// The spec targeted a single object
590        const SINGLE = raw::GIT_REVPARSE_SINGLE as u32;
591        /// The spec targeted a range of commits
592        const RANGE = raw::GIT_REVPARSE_RANGE as u32;
593        /// The spec used the `...` operator, which invokes special semantics.
594        const MERGE_BASE = raw::GIT_REVPARSE_MERGE_BASE as u32;
595    }
596}
597
598impl RevparseMode {
599    is_bit_set!(is_no_single, RevparseMode::SINGLE);
600    is_bit_set!(is_range, RevparseMode::RANGE);
601    is_bit_set!(is_merge_base, RevparseMode::MERGE_BASE);
602}
603
604bitflags! {
605    /// The results of `merge_analysis` indicating the merge opportunities.
606    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
607    pub struct MergeAnalysis: u32 {
608        /// No merge is possible.
609        const ANALYSIS_NONE = raw::GIT_MERGE_ANALYSIS_NONE as u32;
610        /// A "normal" merge; both HEAD and the given merge input have diverged
611        /// from their common ancestor. The divergent commits must be merged.
612        const ANALYSIS_NORMAL = raw::GIT_MERGE_ANALYSIS_NORMAL as u32;
613        /// All given merge inputs are reachable from HEAD, meaning the
614        /// repository is up-to-date and no merge needs to be performed.
615        const ANALYSIS_UP_TO_DATE = raw::GIT_MERGE_ANALYSIS_UP_TO_DATE as u32;
616        /// The given merge input is a fast-forward from HEAD and no merge
617        /// needs to be performed.  Instead, the client can check out the
618        /// given merge input.
619        const ANALYSIS_FASTFORWARD = raw::GIT_MERGE_ANALYSIS_FASTFORWARD as u32;
620        /// The HEAD of the current repository is "unborn" and does not point to
621        /// a valid commit.  No merge can be performed, but the caller may wish
622        /// to simply set HEAD to the target commit(s).
623        const ANALYSIS_UNBORN = raw::GIT_MERGE_ANALYSIS_UNBORN as u32;
624    }
625}
626
627impl MergeAnalysis {
628    is_bit_set!(is_none, MergeAnalysis::ANALYSIS_NONE);
629    is_bit_set!(is_normal, MergeAnalysis::ANALYSIS_NORMAL);
630    is_bit_set!(is_up_to_date, MergeAnalysis::ANALYSIS_UP_TO_DATE);
631    is_bit_set!(is_fast_forward, MergeAnalysis::ANALYSIS_FASTFORWARD);
632    is_bit_set!(is_unborn, MergeAnalysis::ANALYSIS_UNBORN);
633}
634
635bitflags! {
636    /// The user's stated preference for merges.
637    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
638    pub struct MergePreference: u32 {
639        /// No configuration was found that suggests a preferred behavior for
640        /// merge.
641        const NONE = raw::GIT_MERGE_PREFERENCE_NONE as u32;
642        /// There is a `merge.ff=false` configuration setting, suggesting that
643        /// the user does not want to allow a fast-forward merge.
644        const NO_FAST_FORWARD = raw::GIT_MERGE_PREFERENCE_NO_FASTFORWARD as u32;
645        /// There is a `merge.ff=only` configuration setting, suggesting that
646        /// the user only wants fast-forward merges.
647        const FASTFORWARD_ONLY = raw::GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY as u32;
648    }
649}
650
651impl MergePreference {
652    is_bit_set!(is_none, MergePreference::NONE);
653    is_bit_set!(is_no_fast_forward, MergePreference::NO_FAST_FORWARD);
654    is_bit_set!(is_fastforward_only, MergePreference::FASTFORWARD_ONLY);
655}
656
657bitflags! {
658    /// Flags controlling the behavior of ODB lookup operations
659    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
660    pub struct OdbLookupFlags: u32 {
661        /// Don't call `git_odb_refresh` if the lookup fails. Useful when doing
662        /// a batch of lookup operations for objects that may legitimately not
663        /// exist. When using this flag, you may wish to manually call
664        /// `git_odb_refresh` before processing a batch of objects.
665        const NO_REFRESH = raw::GIT_ODB_LOOKUP_NO_REFRESH as u32;
666    }
667}
668
669bitflags! {
670    /// How to handle reference updates.
671    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
672    pub struct RemoteUpdateFlags: u32 {
673       /// Write the fetch results to FETCH_HEAD.
674       const UPDATE_FETCHHEAD = raw::GIT_REMOTE_UPDATE_FETCHHEAD as u32;
675       /// Report unchanged tips in the update_tips callback.
676       const REPORT_UNCHANGED = raw::GIT_REMOTE_UPDATE_REPORT_UNCHANGED as u32;
677    }
678}
679
680#[cfg(test)]
681#[macro_use]
682mod test;
683#[macro_use]
684mod panic;
685mod attr;
686mod call;
687mod util;
688
689pub mod build;
690pub mod cert;
691pub mod oid_array;
692pub mod opts;
693pub mod string_array;
694pub mod transport;
695
696mod apply;
697mod blame;
698mod blob;
699mod branch;
700mod buf;
701mod cherrypick;
702mod commit;
703mod config;
704mod cred;
705mod describe;
706mod diff;
707mod email;
708mod error;
709mod index;
710mod indexer;
711mod mailmap;
712mod mempack;
713mod merge;
714mod message;
715mod note;
716mod object;
717mod odb;
718mod oid;
719mod packbuilder;
720mod patch;
721mod pathspec;
722mod proxy_options;
723mod push_update;
724mod rebase;
725mod reference;
726mod reflog;
727mod refspec;
728mod remote;
729mod remote_callbacks;
730mod repo;
731mod revert;
732mod revspec;
733mod revwalk;
734mod signature;
735mod stash;
736mod status;
737mod submodule;
738mod tag;
739mod tagforeach;
740mod time;
741mod tracing;
742mod transaction;
743mod tree;
744mod treebuilder;
745mod version;
746mod worktree;
747
748fn init() {
749    static INIT: Once = Once::new();
750
751    INIT.call_once(|| {
752        openssl_env_init();
753    });
754
755    raw::init();
756}
757
758#[cfg(all(
759    unix,
760    not(target_os = "macos"),
761    not(target_os = "ios"),
762    feature = "https"
763))]
764fn openssl_env_init() {
765    // Currently, libgit2 leverages OpenSSL for SSL support when cloning
766    // repositories over HTTPS. This means that we're picking up an OpenSSL
767    // dependency on non-Windows platforms (where it has its own HTTPS
768    // subsystem). As a result, we need to link to OpenSSL.
769    //
770    // Now actually *linking* to OpenSSL isn't so hard. We just need to make
771    // sure to use pkg-config to discover any relevant system dependencies for
772    // differences between distributions like CentOS and Ubuntu. The actual
773    // trickiness comes about when we start *distributing* the resulting
774    // binaries. Currently Cargo is distributed in binary form as nightlies,
775    // which means we're distributing a binary with OpenSSL linked in.
776    //
777    // For historical reasons, the Linux nightly builder is running a CentOS
778    // distribution in order to have as much ABI compatibility with other
779    // distributions as possible. Sadly, however, this compatibility does not
780    // extend to OpenSSL. Currently OpenSSL has two major versions, 0.9 and 1.0,
781    // which are incompatible (many ABI differences). The CentOS builder we
782    // build on has version 1.0, as do most distributions today. Some still have
783    // 0.9, however. This means that if we are to distribute the binaries built
784    // by the CentOS machine, we would only be compatible with OpenSSL 1.0 and
785    // we would fail to run (a dynamic linker error at runtime) on systems with
786    // only 9.8 installed (hopefully).
787    //
788    // But wait, the plot thickens! Apparently CentOS has dubbed their OpenSSL
789    // library as `libssl.so.10`, notably the `10` is included at the end. On
790    // the other hand Ubuntu, for example, only distributes `libssl.so`. This
791    // means that the binaries created at CentOS are hard-wired to probe for a
792    // file called `libssl.so.10` at runtime (using the LD_LIBRARY_PATH), which
793    // will not be found on ubuntu. The conclusion of this is that binaries
794    // built on CentOS cannot be distributed to Ubuntu and run successfully.
795    //
796    // There are a number of sneaky things we could do, including, but not
797    // limited to:
798    //
799    // 1. Create a shim program which runs "just before" cargo runs. The
800    //    responsibility of this shim program would be to locate `libssl.so`,
801    //    whatever it's called, on the current system, make sure there's a
802    //    symlink *somewhere* called `libssl.so.10`, and then set up
803    //    LD_LIBRARY_PATH and run the actual cargo.
804    //
805    //    This approach definitely seems unconventional, and is borderline
806    //    overkill for this problem. It's also dubious if we can find a
807    //    libssl.so reliably on the target system.
808    //
809    // 2. Somehow re-work the CentOS installation so that the linked-against
810    //    library is called libssl.so instead of libssl.so.10
811    //
812    //    The problem with this approach is that systems with 0.9 installed will
813    //    start to silently fail, due to also having libraries called libssl.so
814    //    (probably symlinked under a more appropriate version).
815    //
816    // 3. Compile Cargo against both OpenSSL 1.0 *and* OpenSSL 0.9, and
817    //    distribute both. Also make sure that the linked-against name of the
818    //    library is `libssl.so`. At runtime we determine which version is
819    //    installed, and we then the appropriate binary.
820    //
821    //    This approach clearly has drawbacks in terms of infrastructure and
822    //    feasibility.
823    //
824    // 4. Build a nightly of Cargo for each distribution we'd like to support.
825    //    You would then pick the appropriate Cargo nightly to install locally.
826    //
827    // So, with all this in mind, the decision was made to *statically* link
828    // OpenSSL. This solves any problem of relying on a downstream OpenSSL
829    // version being available. This does, however, open a can of worms related
830    // to security issues. It's generally a good idea to dynamically link
831    // OpenSSL as you'll get security updates over time without having to do
832    // anything (the system administrator will update the local openssl
833    // package). By statically linking, we're forfeiting this feature.
834    //
835    // The conclusion was made it is likely appropriate for the Cargo nightlies
836    // to statically link OpenSSL, but highly encourage distributions and
837    // packagers of Cargo to dynamically link OpenSSL. Packagers are targeting
838    // one system and are distributing to only that system, so none of the
839    // problems mentioned above would arise.
840    //
841    // In order to support this, a new package was made: openssl-static-sys.
842    // This package currently performs a fairly simple task:
843    //
844    // 1. Run pkg-config to discover where openssl is installed.
845    // 2. If openssl is installed in a nonstandard location, *and* static copies
846    //    of the libraries are available, copy them to $OUT_DIR.
847    //
848    // This library will bring in libssl.a and libcrypto.a into the local build,
849    // allowing them to be picked up by this crate. This allows us to configure
850    // our own buildbots to have pkg-config point to these local pre-built
851    // copies of a static OpenSSL (with very few dependencies) while allowing
852    // most other builds of Cargo to naturally dynamically link OpenSSL.
853    //
854    // So in summary, if you're with me so far, we've statically linked OpenSSL
855    // to the Cargo binary (or any binary, for that matter) and we're ready to
856    // distribute it to *all* linux distributions. Remember that our original
857    // intent for openssl was for HTTPS support, which implies that we need some
858    // for of CA certificate store to validate certificates. This is normally
859    // installed in a standard system location.
860    //
861    // Unfortunately, as one might imagine, OpenSSL is configured for where this
862    // standard location is at *build time*, but it often varies widely
863    // per-system. Consequently, it was discovered that OpenSSL will respect the
864    // SSL_CERT_FILE and SSL_CERT_DIR environment variables in order to assist
865    // in discovering the location of this file (hurray!).
866    //
867    // So, finally getting to the point, this function solely exists to support
868    // our static builds of OpenSSL by probing for the "standard system
869    // location" of certificates and setting relevant environment variable to
870    // point to them.
871    //
872    // Ah, and as a final note, this is only a problem on Linux, not on OS X. On
873    // OS X the OpenSSL binaries are stable enough that we can just rely on
874    // dynamic linkage (plus they have some weird modifications to OpenSSL which
875    // means we wouldn't want to link statically).
876    openssl_probe::init_ssl_cert_env_vars();
877}
878
879#[cfg(any(
880    windows,
881    target_os = "macos",
882    target_os = "ios",
883    not(feature = "https")
884))]
885fn openssl_env_init() {}
886
887unsafe fn opt_bytes<'a, T>(_anchor: &'a T, c: *const libc::c_char) -> Option<&'a [u8]> {
888    if c.is_null() {
889        None
890    } else {
891        Some(CStr::from_ptr(c).to_bytes())
892    }
893}
894
895fn opt_cstr<T: IntoCString>(o: Option<T>) -> Result<Option<CString>, Error> {
896    match o {
897        Some(s) => s.into_c_string().map(Some),
898        None => Ok(None),
899    }
900}
901
902impl ObjectType {
903    /// Convert an object type to its string representation.
904    pub fn str(&self) -> &'static str {
905        unsafe {
906            let ptr = call!(raw::git_object_type2string(*self)) as *const _;
907            let data = CStr::from_ptr(ptr).to_bytes();
908            str::from_utf8(data).unwrap()
909        }
910    }
911
912    /// Determine if the given git_object_t is a valid loose object type.
913    pub fn is_loose(&self) -> bool {
914        unsafe { call!(raw::git_object_typeisloose(*self)) == 1 }
915    }
916
917    /// Convert a raw git_object_t to an ObjectType
918    pub fn from_raw(raw: raw::git_object_t) -> Option<ObjectType> {
919        match raw {
920            raw::GIT_OBJECT_ANY => Some(ObjectType::Any),
921            raw::GIT_OBJECT_COMMIT => Some(ObjectType::Commit),
922            raw::GIT_OBJECT_TREE => Some(ObjectType::Tree),
923            raw::GIT_OBJECT_BLOB => Some(ObjectType::Blob),
924            raw::GIT_OBJECT_TAG => Some(ObjectType::Tag),
925            _ => None,
926        }
927    }
928
929    /// Convert this kind into its raw representation
930    pub fn raw(&self) -> raw::git_object_t {
931        call::convert(self)
932    }
933
934    /// Convert a string object type representation to its object type.
935    pub fn from_str(s: &str) -> Option<ObjectType> {
936        let raw = unsafe { call!(raw::git_object_string2type(CString::new(s).unwrap())) };
937        ObjectType::from_raw(raw)
938    }
939}
940
941impl fmt::Display for ObjectType {
942    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
943        self.str().fmt(f)
944    }
945}
946
947impl ReferenceType {
948    /// Convert an object type to its string representation.
949    pub fn str(&self) -> &'static str {
950        match self {
951            ReferenceType::Direct => "direct",
952            ReferenceType::Symbolic => "symbolic",
953        }
954    }
955
956    /// Convert a raw git_reference_t to a ReferenceType.
957    pub fn from_raw(raw: raw::git_reference_t) -> Option<ReferenceType> {
958        match raw {
959            raw::GIT_REFERENCE_DIRECT => Some(ReferenceType::Direct),
960            raw::GIT_REFERENCE_SYMBOLIC => Some(ReferenceType::Symbolic),
961            _ => None,
962        }
963    }
964}
965
966impl fmt::Display for ReferenceType {
967    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
968        self.str().fmt(f)
969    }
970}
971
972impl ConfigLevel {
973    /// Converts a raw configuration level to a ConfigLevel
974    pub fn from_raw(raw: raw::git_config_level_t) -> ConfigLevel {
975        match raw {
976            raw::GIT_CONFIG_LEVEL_PROGRAMDATA => ConfigLevel::ProgramData,
977            raw::GIT_CONFIG_LEVEL_SYSTEM => ConfigLevel::System,
978            raw::GIT_CONFIG_LEVEL_XDG => ConfigLevel::XDG,
979            raw::GIT_CONFIG_LEVEL_GLOBAL => ConfigLevel::Global,
980            raw::GIT_CONFIG_LEVEL_LOCAL => ConfigLevel::Local,
981            raw::GIT_CONFIG_LEVEL_WORKTREE => ConfigLevel::Worktree,
982            raw::GIT_CONFIG_LEVEL_APP => ConfigLevel::App,
983            raw::GIT_CONFIG_HIGHEST_LEVEL => ConfigLevel::Highest,
984            n => panic!("unknown config level: {}", n),
985        }
986    }
987}
988
989impl SubmoduleIgnore {
990    /// Converts a [`raw::git_submodule_ignore_t`] to a [`SubmoduleIgnore`]
991    pub fn from_raw(raw: raw::git_submodule_ignore_t) -> Self {
992        match raw {
993            raw::GIT_SUBMODULE_IGNORE_UNSPECIFIED => SubmoduleIgnore::Unspecified,
994            raw::GIT_SUBMODULE_IGNORE_NONE => SubmoduleIgnore::None,
995            raw::GIT_SUBMODULE_IGNORE_UNTRACKED => SubmoduleIgnore::Untracked,
996            raw::GIT_SUBMODULE_IGNORE_DIRTY => SubmoduleIgnore::Dirty,
997            raw::GIT_SUBMODULE_IGNORE_ALL => SubmoduleIgnore::All,
998            n => panic!("unknown submodule ignore rule: {}", n),
999        }
1000    }
1001}
1002
1003impl SubmoduleUpdate {
1004    /// Converts a [`raw::git_submodule_update_t`] to a [`SubmoduleUpdate`]
1005    pub fn from_raw(raw: raw::git_submodule_update_t) -> Self {
1006        match raw {
1007            raw::GIT_SUBMODULE_UPDATE_CHECKOUT => SubmoduleUpdate::Checkout,
1008            raw::GIT_SUBMODULE_UPDATE_REBASE => SubmoduleUpdate::Rebase,
1009            raw::GIT_SUBMODULE_UPDATE_MERGE => SubmoduleUpdate::Merge,
1010            raw::GIT_SUBMODULE_UPDATE_NONE => SubmoduleUpdate::None,
1011            raw::GIT_SUBMODULE_UPDATE_DEFAULT => SubmoduleUpdate::Default,
1012            n => panic!("unknown submodule update strategy: {}", n),
1013        }
1014    }
1015}
1016
1017bitflags! {
1018    /// Status flags for a single file
1019    ///
1020    /// A combination of these values will be returned to indicate the status of
1021    /// a file.  Status compares the working directory, the index, and the
1022    /// current HEAD of the repository.  The `STATUS_INDEX_*` set of flags
1023    /// represents the status of file in the index relative to the HEAD, and the
1024    /// `STATUS_WT_*` set of flags represent the status of the file in the
1025    /// working directory relative to the index.
1026    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1027    pub struct Status: u32 {
1028        #[allow(missing_docs)]
1029        const CURRENT = raw::GIT_STATUS_CURRENT as u32;
1030
1031        #[allow(missing_docs)]
1032        const INDEX_NEW = raw::GIT_STATUS_INDEX_NEW as u32;
1033        #[allow(missing_docs)]
1034        const INDEX_MODIFIED = raw::GIT_STATUS_INDEX_MODIFIED as u32;
1035        #[allow(missing_docs)]
1036        const INDEX_DELETED = raw::GIT_STATUS_INDEX_DELETED as u32;
1037        #[allow(missing_docs)]
1038        const INDEX_RENAMED = raw::GIT_STATUS_INDEX_RENAMED as u32;
1039        #[allow(missing_docs)]
1040        const INDEX_TYPECHANGE = raw::GIT_STATUS_INDEX_TYPECHANGE as u32;
1041
1042        #[allow(missing_docs)]
1043        const WT_NEW = raw::GIT_STATUS_WT_NEW as u32;
1044        #[allow(missing_docs)]
1045        const WT_MODIFIED = raw::GIT_STATUS_WT_MODIFIED as u32;
1046        #[allow(missing_docs)]
1047        const WT_DELETED = raw::GIT_STATUS_WT_DELETED as u32;
1048        #[allow(missing_docs)]
1049        const WT_TYPECHANGE = raw::GIT_STATUS_WT_TYPECHANGE as u32;
1050        #[allow(missing_docs)]
1051        const WT_RENAMED = raw::GIT_STATUS_WT_RENAMED as u32;
1052
1053        #[allow(missing_docs)]
1054        const IGNORED = raw::GIT_STATUS_IGNORED as u32;
1055        #[allow(missing_docs)]
1056        const CONFLICTED = raw::GIT_STATUS_CONFLICTED as u32;
1057    }
1058}
1059
1060impl Status {
1061    is_bit_set!(is_index_new, Status::INDEX_NEW);
1062    is_bit_set!(is_index_modified, Status::INDEX_MODIFIED);
1063    is_bit_set!(is_index_deleted, Status::INDEX_DELETED);
1064    is_bit_set!(is_index_renamed, Status::INDEX_RENAMED);
1065    is_bit_set!(is_index_typechange, Status::INDEX_TYPECHANGE);
1066    is_bit_set!(is_wt_new, Status::WT_NEW);
1067    is_bit_set!(is_wt_modified, Status::WT_MODIFIED);
1068    is_bit_set!(is_wt_deleted, Status::WT_DELETED);
1069    is_bit_set!(is_wt_typechange, Status::WT_TYPECHANGE);
1070    is_bit_set!(is_wt_renamed, Status::WT_RENAMED);
1071    is_bit_set!(is_ignored, Status::IGNORED);
1072    is_bit_set!(is_conflicted, Status::CONFLICTED);
1073}
1074
1075bitflags! {
1076    /// Mode options for RepositoryInitOptions
1077    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1078    pub struct RepositoryInitMode: u32 {
1079        /// Use permissions configured by umask - the default
1080        const SHARED_UMASK = raw::GIT_REPOSITORY_INIT_SHARED_UMASK as u32;
1081        /// Use `--shared=group` behavior, chmod'ing the new repo to be
1082        /// group writable and \"g+sx\" for sticky group assignment
1083        const SHARED_GROUP = raw::GIT_REPOSITORY_INIT_SHARED_GROUP as u32;
1084        /// Use `--shared=all` behavior, adding world readability.
1085        const SHARED_ALL = raw::GIT_REPOSITORY_INIT_SHARED_ALL as u32;
1086    }
1087}
1088
1089impl RepositoryInitMode {
1090    is_bit_set!(is_shared_umask, RepositoryInitMode::SHARED_UMASK);
1091    is_bit_set!(is_shared_group, RepositoryInitMode::SHARED_GROUP);
1092    is_bit_set!(is_shared_all, RepositoryInitMode::SHARED_ALL);
1093}
1094
1095/// What type of change is described by a `DiffDelta`?
1096#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1097pub enum Delta {
1098    /// No changes
1099    Unmodified,
1100    /// Entry does not exist in old version
1101    Added,
1102    /// Entry does not exist in new version
1103    Deleted,
1104    /// Entry content changed between old and new
1105    Modified,
1106    /// Entry was renamed between old and new
1107    Renamed,
1108    /// Entry was copied from another old entry
1109    Copied,
1110    /// Entry is ignored item in workdir
1111    Ignored,
1112    /// Entry is untracked item in workdir
1113    Untracked,
1114    /// Type of entry changed between old and new
1115    Typechange,
1116    /// Entry is unreadable
1117    Unreadable,
1118    /// Entry in the index is conflicted
1119    Conflicted,
1120}
1121
1122/// Valid modes for index and tree entries.
1123#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1124pub enum FileMode {
1125    /// Unreadable
1126    Unreadable,
1127    /// Tree
1128    Tree,
1129    /// Blob
1130    Blob,
1131    /// Group writable blob. Obsolete mode kept for compatibility reasons
1132    BlobGroupWritable,
1133    /// Blob executable
1134    BlobExecutable,
1135    /// Link
1136    Link,
1137    /// Commit
1138    Commit,
1139}
1140
1141impl From<FileMode> for i32 {
1142    fn from(mode: FileMode) -> i32 {
1143        match mode {
1144            FileMode::Unreadable => raw::GIT_FILEMODE_UNREADABLE as i32,
1145            FileMode::Tree => raw::GIT_FILEMODE_TREE as i32,
1146            FileMode::Blob => raw::GIT_FILEMODE_BLOB as i32,
1147            FileMode::BlobGroupWritable => raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE as i32,
1148            FileMode::BlobExecutable => raw::GIT_FILEMODE_BLOB_EXECUTABLE as i32,
1149            FileMode::Link => raw::GIT_FILEMODE_LINK as i32,
1150            FileMode::Commit => raw::GIT_FILEMODE_COMMIT as i32,
1151        }
1152    }
1153}
1154
1155impl From<FileMode> for u32 {
1156    fn from(mode: FileMode) -> u32 {
1157        match mode {
1158            FileMode::Unreadable => raw::GIT_FILEMODE_UNREADABLE as u32,
1159            FileMode::Tree => raw::GIT_FILEMODE_TREE as u32,
1160            FileMode::Blob => raw::GIT_FILEMODE_BLOB as u32,
1161            FileMode::BlobGroupWritable => raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE as u32,
1162            FileMode::BlobExecutable => raw::GIT_FILEMODE_BLOB_EXECUTABLE as u32,
1163            FileMode::Link => raw::GIT_FILEMODE_LINK as u32,
1164            FileMode::Commit => raw::GIT_FILEMODE_COMMIT as u32,
1165        }
1166    }
1167}
1168
1169bitflags! {
1170    /// Return codes for submodule status.
1171    ///
1172    /// A combination of these flags will be returned to describe the status of a
1173    /// submodule.  Depending on the "ignore" property of the submodule, some of
1174    /// the flags may never be returned because they indicate changes that are
1175    /// supposed to be ignored.
1176    ///
1177    /// Submodule info is contained in 4 places: the HEAD tree, the index, config
1178    /// files (both .git/config and .gitmodules), and the working directory.  Any
1179    /// or all of those places might be missing information about the submodule
1180    /// depending on what state the repo is in.  We consider all four places to
1181    /// build the combination of status flags.
1182    ///
1183    /// There are four values that are not really status, but give basic info
1184    /// about what sources of submodule data are available.  These will be
1185    /// returned even if ignore is set to "ALL".
1186    ///
1187    /// * IN_HEAD   - superproject head contains submodule
1188    /// * IN_INDEX  - superproject index contains submodule
1189    /// * IN_CONFIG - superproject gitmodules has submodule
1190    /// * IN_WD     - superproject workdir has submodule
1191    ///
1192    /// The following values will be returned so long as ignore is not "ALL".
1193    ///
1194    /// * INDEX_ADDED       - in index, not in head
1195    /// * INDEX_DELETED     - in head, not in index
1196    /// * INDEX_MODIFIED    - index and head don't match
1197    /// * WD_UNINITIALIZED  - workdir contains empty directory
1198    /// * WD_ADDED          - in workdir, not index
1199    /// * WD_DELETED        - in index, not workdir
1200    /// * WD_MODIFIED       - index and workdir head don't match
1201    ///
1202    /// The following can only be returned if ignore is "NONE" or "UNTRACKED".
1203    ///
1204    /// * WD_INDEX_MODIFIED - submodule workdir index is dirty
1205    /// * WD_WD_MODIFIED    - submodule workdir has modified files
1206    ///
1207    /// Lastly, the following will only be returned for ignore "NONE".
1208    ///
1209    /// * WD_UNTRACKED      - workdir contains untracked files
1210    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1211    pub struct SubmoduleStatus: u32 {
1212        #[allow(missing_docs)]
1213        const IN_HEAD = raw::GIT_SUBMODULE_STATUS_IN_HEAD as u32;
1214        #[allow(missing_docs)]
1215        const IN_INDEX = raw::GIT_SUBMODULE_STATUS_IN_INDEX as u32;
1216        #[allow(missing_docs)]
1217        const IN_CONFIG = raw::GIT_SUBMODULE_STATUS_IN_CONFIG as u32;
1218        #[allow(missing_docs)]
1219        const IN_WD = raw::GIT_SUBMODULE_STATUS_IN_WD as u32;
1220        #[allow(missing_docs)]
1221        const INDEX_ADDED = raw::GIT_SUBMODULE_STATUS_INDEX_ADDED as u32;
1222        #[allow(missing_docs)]
1223        const INDEX_DELETED = raw::GIT_SUBMODULE_STATUS_INDEX_DELETED as u32;
1224        #[allow(missing_docs)]
1225        const INDEX_MODIFIED = raw::GIT_SUBMODULE_STATUS_INDEX_MODIFIED as u32;
1226        #[allow(missing_docs)]
1227        const WD_UNINITIALIZED =
1228                raw::GIT_SUBMODULE_STATUS_WD_UNINITIALIZED as u32;
1229        #[allow(missing_docs)]
1230        const WD_ADDED = raw::GIT_SUBMODULE_STATUS_WD_ADDED as u32;
1231        #[allow(missing_docs)]
1232        const WD_DELETED = raw::GIT_SUBMODULE_STATUS_WD_DELETED as u32;
1233        #[allow(missing_docs)]
1234        const WD_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_MODIFIED as u32;
1235        #[allow(missing_docs)]
1236        const WD_INDEX_MODIFIED =
1237                raw::GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED as u32;
1238        #[allow(missing_docs)]
1239        const WD_WD_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_WD_MODIFIED as u32;
1240        #[allow(missing_docs)]
1241        const WD_UNTRACKED = raw::GIT_SUBMODULE_STATUS_WD_UNTRACKED as u32;
1242    }
1243}
1244
1245impl SubmoduleStatus {
1246    is_bit_set!(is_in_head, SubmoduleStatus::IN_HEAD);
1247    is_bit_set!(is_in_index, SubmoduleStatus::IN_INDEX);
1248    is_bit_set!(is_in_config, SubmoduleStatus::IN_CONFIG);
1249    is_bit_set!(is_in_wd, SubmoduleStatus::IN_WD);
1250    is_bit_set!(is_index_added, SubmoduleStatus::INDEX_ADDED);
1251    is_bit_set!(is_index_deleted, SubmoduleStatus::INDEX_DELETED);
1252    is_bit_set!(is_index_modified, SubmoduleStatus::INDEX_MODIFIED);
1253    is_bit_set!(is_wd_uninitialized, SubmoduleStatus::WD_UNINITIALIZED);
1254    is_bit_set!(is_wd_added, SubmoduleStatus::WD_ADDED);
1255    is_bit_set!(is_wd_deleted, SubmoduleStatus::WD_DELETED);
1256    is_bit_set!(is_wd_modified, SubmoduleStatus::WD_MODIFIED);
1257    is_bit_set!(is_wd_wd_modified, SubmoduleStatus::WD_WD_MODIFIED);
1258    is_bit_set!(is_wd_untracked, SubmoduleStatus::WD_UNTRACKED);
1259}
1260
1261/// Submodule ignore values
1262///
1263/// These values represent settings for the `submodule.$name.ignore`
1264/// configuration value which says how deeply to look at the working
1265/// directory when getting the submodule status.
1266#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1267pub enum SubmoduleIgnore {
1268    /// Use the submodule's configuration
1269    Unspecified,
1270    /// Any change or untracked file is considered dirty
1271    None,
1272    /// Only dirty if tracked files have changed
1273    Untracked,
1274    /// Only dirty if HEAD has moved
1275    Dirty,
1276    /// Never dirty
1277    All,
1278}
1279
1280/// Submodule update values
1281///
1282/// These values represent settings for the `submodule.$name.update`
1283/// configuration value which says how to handle `git submodule update`
1284/// for this submodule. The value is usually set in the ".gitmodules"
1285/// file and copied to ".git/config" when the submodule is initialized.
1286#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1287pub enum SubmoduleUpdate {
1288    /// The default; when a submodule is updated, checkout the new detached
1289    /// HEAD to the submodule directory.
1290    Checkout,
1291    /// Update by rebasing the current checked out branch onto the commit from
1292    /// the superproject.
1293    Rebase,
1294    /// Update by merging the commit in the superproject into the current
1295    /// checkout out branch of the submodule.
1296    Merge,
1297    /// Do not update this submodule even when the commit in the superproject
1298    /// is updated.
1299    None,
1300    /// Not used except as static initializer when we don't want any particular
1301    /// update rule to be specified.
1302    Default,
1303}
1304
1305bitflags! {
1306    /// ...
1307    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1308    pub struct PathspecFlags: u32 {
1309        /// Use the default pathspec matching configuration.
1310        const DEFAULT = raw::GIT_PATHSPEC_DEFAULT as u32;
1311        /// Force matching to ignore case, otherwise matching will use native
1312        /// case sensitivity of the platform filesystem.
1313        const IGNORE_CASE = raw::GIT_PATHSPEC_IGNORE_CASE as u32;
1314        /// Force case sensitive matches, otherwise match will use the native
1315        /// case sensitivity of the platform filesystem.
1316        const USE_CASE = raw::GIT_PATHSPEC_USE_CASE as u32;
1317        /// Disable glob patterns and just use simple string comparison for
1318        /// matching.
1319        const NO_GLOB = raw::GIT_PATHSPEC_NO_GLOB as u32;
1320        /// Means that match functions return the error code `NotFound` if no
1321        /// matches are found. By default no matches is a success.
1322        const NO_MATCH_ERROR = raw::GIT_PATHSPEC_NO_MATCH_ERROR as u32;
1323        /// Means that the list returned should track which patterns matched
1324        /// which files so that at the end of the match we can identify patterns
1325        /// that did not match any files.
1326        const FIND_FAILURES = raw::GIT_PATHSPEC_FIND_FAILURES as u32;
1327        /// Means that the list returned does not need to keep the actual
1328        /// matching filenames. Use this to just test if there were any matches
1329        /// at all or in combination with `PATHSPEC_FAILURES` to validate a
1330        /// pathspec.
1331        const FAILURES_ONLY = raw::GIT_PATHSPEC_FAILURES_ONLY as u32;
1332    }
1333}
1334
1335impl PathspecFlags {
1336    is_bit_set!(is_default, PathspecFlags::DEFAULT);
1337    is_bit_set!(is_ignore_case, PathspecFlags::IGNORE_CASE);
1338    is_bit_set!(is_use_case, PathspecFlags::USE_CASE);
1339    is_bit_set!(is_no_glob, PathspecFlags::NO_GLOB);
1340    is_bit_set!(is_no_match_error, PathspecFlags::NO_MATCH_ERROR);
1341    is_bit_set!(is_find_failures, PathspecFlags::FIND_FAILURES);
1342    is_bit_set!(is_failures_only, PathspecFlags::FAILURES_ONLY);
1343}
1344
1345impl Default for PathspecFlags {
1346    fn default() -> Self {
1347        PathspecFlags::DEFAULT
1348    }
1349}
1350
1351bitflags! {
1352    /// Types of notifications emitted from checkouts.
1353    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1354    pub struct CheckoutNotificationType: u32 {
1355        /// Notification about a conflict.
1356        const CONFLICT = raw::GIT_CHECKOUT_NOTIFY_CONFLICT as u32;
1357        /// Notification about a dirty file.
1358        const DIRTY = raw::GIT_CHECKOUT_NOTIFY_DIRTY as u32;
1359        /// Notification about an updated file.
1360        const UPDATED = raw::GIT_CHECKOUT_NOTIFY_UPDATED as u32;
1361        /// Notification about an untracked file.
1362        const UNTRACKED = raw::GIT_CHECKOUT_NOTIFY_UNTRACKED as u32;
1363        /// Notification about an ignored file.
1364        const IGNORED = raw::GIT_CHECKOUT_NOTIFY_IGNORED as u32;
1365    }
1366}
1367
1368impl CheckoutNotificationType {
1369    is_bit_set!(is_conflict, CheckoutNotificationType::CONFLICT);
1370    is_bit_set!(is_dirty, CheckoutNotificationType::DIRTY);
1371    is_bit_set!(is_updated, CheckoutNotificationType::UPDATED);
1372    is_bit_set!(is_untracked, CheckoutNotificationType::UNTRACKED);
1373    is_bit_set!(is_ignored, CheckoutNotificationType::IGNORED);
1374}
1375
1376/// Possible output formats for diff data
1377#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1378pub enum DiffFormat {
1379    /// full git diff
1380    Patch,
1381    /// just the headers of the patch
1382    PatchHeader,
1383    /// like git diff --raw
1384    Raw,
1385    /// like git diff --name-only
1386    NameOnly,
1387    /// like git diff --name-status
1388    NameStatus,
1389    /// git diff as used by git patch-id
1390    PatchId,
1391}
1392
1393bitflags! {
1394    /// Formatting options for diff stats
1395    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1396    pub struct DiffStatsFormat: raw::git_diff_stats_format_t {
1397        /// Don't generate any stats
1398        const NONE = raw::GIT_DIFF_STATS_NONE;
1399        /// Equivalent of `--stat` in git
1400        const FULL = raw::GIT_DIFF_STATS_FULL;
1401        /// Equivalent of `--shortstat` in git
1402        const SHORT = raw::GIT_DIFF_STATS_SHORT;
1403        /// Equivalent of `--numstat` in git
1404        const NUMBER = raw::GIT_DIFF_STATS_NUMBER;
1405        /// Extended header information such as creations, renames and mode
1406        /// changes, equivalent of `--summary` in git
1407        const INCLUDE_SUMMARY = raw::GIT_DIFF_STATS_INCLUDE_SUMMARY;
1408    }
1409}
1410
1411impl DiffStatsFormat {
1412    is_bit_set!(is_none, DiffStatsFormat::NONE);
1413    is_bit_set!(is_full, DiffStatsFormat::FULL);
1414    is_bit_set!(is_short, DiffStatsFormat::SHORT);
1415    is_bit_set!(is_number, DiffStatsFormat::NUMBER);
1416    is_bit_set!(is_include_summary, DiffStatsFormat::INCLUDE_SUMMARY);
1417}
1418
1419/// Automatic tag following options.
1420#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1421pub enum AutotagOption {
1422    /// Use the setting from the remote's configuration
1423    Unspecified,
1424    /// Ask the server for tags pointing to objects we're already downloading
1425    Auto,
1426    /// Don't ask for any tags beyond the refspecs
1427    None,
1428    /// Ask for all the tags
1429    All,
1430}
1431
1432/// Configuration for how pruning is done on a fetch
1433#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1434pub enum FetchPrune {
1435    /// Use the setting from the configuration
1436    Unspecified,
1437    /// Force pruning on
1438    On,
1439    /// Force pruning off
1440    Off,
1441}
1442
1443#[allow(missing_docs)]
1444#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1445pub enum StashApplyProgress {
1446    /// None
1447    None,
1448    /// Loading the stashed data from the object database
1449    LoadingStash,
1450    /// The stored index is being analyzed
1451    AnalyzeIndex,
1452    /// The modified files are being analyzed
1453    AnalyzeModified,
1454    /// The untracked and ignored files are being analyzed
1455    AnalyzeUntracked,
1456    /// The untracked files are being written to disk
1457    CheckoutUntracked,
1458    /// The modified files are being written to disk
1459    CheckoutModified,
1460    /// The stash was applied successfully
1461    Done,
1462}
1463
1464bitflags! {
1465    #[allow(missing_docs)]
1466    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1467    pub struct StashApplyFlags: u32 {
1468        #[allow(missing_docs)]
1469        const DEFAULT = raw::GIT_STASH_APPLY_DEFAULT as u32;
1470        /// Try to reinstate not only the working tree's changes,
1471        /// but also the index's changes.
1472        const REINSTATE_INDEX = raw::GIT_STASH_APPLY_REINSTATE_INDEX as u32;
1473    }
1474}
1475
1476impl StashApplyFlags {
1477    is_bit_set!(is_default, StashApplyFlags::DEFAULT);
1478    is_bit_set!(is_reinstate_index, StashApplyFlags::REINSTATE_INDEX);
1479}
1480
1481impl Default for StashApplyFlags {
1482    fn default() -> Self {
1483        StashApplyFlags::DEFAULT
1484    }
1485}
1486
1487bitflags! {
1488    #[allow(missing_docs)]
1489    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1490    pub struct StashFlags: u32 {
1491        #[allow(missing_docs)]
1492        const DEFAULT = raw::GIT_STASH_DEFAULT as u32;
1493        /// All changes already added to the index are left intact in
1494        /// the working directory
1495        const KEEP_INDEX = raw::GIT_STASH_KEEP_INDEX as u32;
1496        /// All untracked files are also stashed and then cleaned up
1497        /// from the working directory
1498        const INCLUDE_UNTRACKED = raw::GIT_STASH_INCLUDE_UNTRACKED as u32;
1499        /// All ignored files are also stashed and then cleaned up from
1500        /// the working directory
1501        const INCLUDE_IGNORED = raw::GIT_STASH_INCLUDE_IGNORED as u32;
1502        /// All changes in the index and working directory are left intact
1503        const KEEP_ALL = raw::GIT_STASH_KEEP_ALL as u32;
1504    }
1505}
1506
1507impl StashFlags {
1508    is_bit_set!(is_default, StashFlags::DEFAULT);
1509    is_bit_set!(is_keep_index, StashFlags::KEEP_INDEX);
1510    is_bit_set!(is_include_untracked, StashFlags::INCLUDE_UNTRACKED);
1511    is_bit_set!(is_include_ignored, StashFlags::INCLUDE_IGNORED);
1512}
1513
1514impl Default for StashFlags {
1515    fn default() -> Self {
1516        StashFlags::DEFAULT
1517    }
1518}
1519
1520bitflags! {
1521    #[allow(missing_docs)]
1522    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1523    pub struct AttrCheckFlags: u32 {
1524        /// Check the working directory, then the index.
1525        const FILE_THEN_INDEX = raw::GIT_ATTR_CHECK_FILE_THEN_INDEX as u32;
1526        /// Check the index, then the working directory.
1527        const INDEX_THEN_FILE = raw::GIT_ATTR_CHECK_INDEX_THEN_FILE as u32;
1528        /// Check the index only.
1529        const INDEX_ONLY = raw::GIT_ATTR_CHECK_INDEX_ONLY as u32;
1530        /// Do not use the system gitattributes file.
1531        const NO_SYSTEM = raw::GIT_ATTR_CHECK_NO_SYSTEM as u32;
1532    }
1533}
1534
1535impl Default for AttrCheckFlags {
1536    fn default() -> Self {
1537        AttrCheckFlags::FILE_THEN_INDEX
1538    }
1539}
1540
1541bitflags! {
1542    #[allow(missing_docs)]
1543    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1544    pub struct DiffFlags: u32 {
1545        /// File(s) treated as binary data.
1546        const BINARY = raw::GIT_DIFF_FLAG_BINARY as u32;
1547        /// File(s) treated as text data.
1548        const NOT_BINARY = raw::GIT_DIFF_FLAG_NOT_BINARY as u32;
1549        /// `id` value is known correct.
1550        const VALID_ID = raw::GIT_DIFF_FLAG_VALID_ID as u32;
1551        /// File exists at this side of the delta.
1552        const EXISTS = raw::GIT_DIFF_FLAG_EXISTS as u32;
1553    }
1554}
1555
1556impl DiffFlags {
1557    is_bit_set!(is_binary, DiffFlags::BINARY);
1558    is_bit_set!(is_not_binary, DiffFlags::NOT_BINARY);
1559    is_bit_set!(has_valid_id, DiffFlags::VALID_ID);
1560    is_bit_set!(exists, DiffFlags::EXISTS);
1561}
1562
1563bitflags! {
1564    /// Options for [`Reference::normalize_name`].
1565    #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
1566    pub struct ReferenceFormat: u32 {
1567        /// No particular normalization.
1568        const NORMAL = raw::GIT_REFERENCE_FORMAT_NORMAL as u32;
1569        /// Control whether one-level refname are accepted (i.e., refnames that
1570        /// do not contain multiple `/`-separated components). Those are
1571        /// expected to be written only using uppercase letters and underscore
1572        /// (e.g. `HEAD`, `FETCH_HEAD`).
1573        const ALLOW_ONELEVEL = raw::GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL as u32;
1574        /// Interpret the provided name as a reference pattern for a refspec (as
1575        /// used with remote repositories). If this option is enabled, the name
1576        /// is allowed to contain a single `*` in place of a full pathname
1577        /// components (e.g., `foo/*/bar` but not `foo/bar*`).
1578        const REFSPEC_PATTERN = raw::GIT_REFERENCE_FORMAT_REFSPEC_PATTERN as u32;
1579        /// Interpret the name as part of a refspec in shorthand form so the
1580        /// `ALLOW_ONELEVEL` naming rules aren't enforced and `main` becomes a
1581        /// valid name.
1582        const REFSPEC_SHORTHAND = raw::GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND as u32;
1583    }
1584}
1585
1586impl ReferenceFormat {
1587    is_bit_set!(is_allow_onelevel, ReferenceFormat::ALLOW_ONELEVEL);
1588    is_bit_set!(is_refspec_pattern, ReferenceFormat::REFSPEC_PATTERN);
1589    is_bit_set!(is_refspec_shorthand, ReferenceFormat::REFSPEC_SHORTHAND);
1590}
1591
1592impl Default for ReferenceFormat {
1593    fn default() -> Self {
1594        ReferenceFormat::NORMAL
1595    }
1596}
1597
1598#[cfg(test)]
1599mod tests {
1600    use super::{FileMode, ObjectType};
1601
1602    #[test]
1603    fn convert() {
1604        assert_eq!(ObjectType::Blob.str(), "blob");
1605        assert_eq!(ObjectType::from_str("blob"), Some(ObjectType::Blob));
1606        assert!(ObjectType::Blob.is_loose());
1607    }
1608
1609    #[test]
1610    fn convert_filemode() {
1611        assert_eq!(i32::from(FileMode::Blob), 0o100644);
1612        assert_eq!(i32::from(FileMode::BlobGroupWritable), 0o100664);
1613        assert_eq!(i32::from(FileMode::BlobExecutable), 0o100755);
1614        assert_eq!(u32::from(FileMode::Blob), 0o100644);
1615        assert_eq!(u32::from(FileMode::BlobGroupWritable), 0o100664);
1616        assert_eq!(u32::from(FileMode::BlobExecutable), 0o100755);
1617    }
1618
1619    #[test]
1620    fn bitflags_partial_eq() {
1621        use super::{
1622            AttrCheckFlags, CheckoutNotificationType, CredentialType, DiffFlags, DiffStatsFormat,
1623            IndexAddOption, IndexEntryExtendedFlag, IndexEntryFlag, MergeAnalysis, MergePreference,
1624            OdbLookupFlags, PathspecFlags, ReferenceFormat, RepositoryInitMode,
1625            RepositoryOpenFlags, RevparseMode, Sort, StashApplyFlags, StashFlags, Status,
1626            SubmoduleStatus,
1627        };
1628
1629        assert_eq!(
1630            AttrCheckFlags::FILE_THEN_INDEX,
1631            AttrCheckFlags::FILE_THEN_INDEX
1632        );
1633        assert_eq!(
1634            CheckoutNotificationType::CONFLICT,
1635            CheckoutNotificationType::CONFLICT
1636        );
1637        assert_eq!(
1638            CredentialType::USER_PASS_PLAINTEXT,
1639            CredentialType::USER_PASS_PLAINTEXT
1640        );
1641        assert_eq!(DiffFlags::BINARY, DiffFlags::BINARY);
1642        assert_eq!(
1643            DiffStatsFormat::INCLUDE_SUMMARY,
1644            DiffStatsFormat::INCLUDE_SUMMARY
1645        );
1646        assert_eq!(
1647            IndexAddOption::CHECK_PATHSPEC,
1648            IndexAddOption::CHECK_PATHSPEC
1649        );
1650        assert_eq!(
1651            IndexEntryExtendedFlag::INTENT_TO_ADD,
1652            IndexEntryExtendedFlag::INTENT_TO_ADD
1653        );
1654        assert_eq!(IndexEntryFlag::EXTENDED, IndexEntryFlag::EXTENDED);
1655        assert_eq!(
1656            MergeAnalysis::ANALYSIS_FASTFORWARD,
1657            MergeAnalysis::ANALYSIS_FASTFORWARD
1658        );
1659        assert_eq!(
1660            MergePreference::FASTFORWARD_ONLY,
1661            MergePreference::FASTFORWARD_ONLY
1662        );
1663        assert_eq!(OdbLookupFlags::NO_REFRESH, OdbLookupFlags::NO_REFRESH);
1664        assert_eq!(PathspecFlags::FAILURES_ONLY, PathspecFlags::FAILURES_ONLY);
1665        assert_eq!(
1666            ReferenceFormat::ALLOW_ONELEVEL,
1667            ReferenceFormat::ALLOW_ONELEVEL
1668        );
1669        assert_eq!(
1670            RepositoryInitMode::SHARED_ALL,
1671            RepositoryInitMode::SHARED_ALL
1672        );
1673        assert_eq!(RepositoryOpenFlags::CROSS_FS, RepositoryOpenFlags::CROSS_FS);
1674        assert_eq!(RevparseMode::RANGE, RevparseMode::RANGE);
1675        assert_eq!(Sort::REVERSE, Sort::REVERSE);
1676        assert_eq!(
1677            StashApplyFlags::REINSTATE_INDEX,
1678            StashApplyFlags::REINSTATE_INDEX
1679        );
1680        assert_eq!(StashFlags::INCLUDE_IGNORED, StashFlags::INCLUDE_IGNORED);
1681        assert_eq!(Status::WT_MODIFIED, Status::WT_MODIFIED);
1682        assert_eq!(SubmoduleStatus::WD_ADDED, SubmoduleStatus::WD_ADDED);
1683    }
1684}