summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/head/mod.rs
blob: 094e78a86427c56f0874096abfb7d61d6ec96bc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//!
use std::convert::TryInto;

use gix_hash::ObjectId;
use gix_ref::FullNameRef;

use crate::{
    ext::{ObjectIdExt, ReferenceExt},
    Head,
};

/// Represents the kind of `HEAD` reference.
#[derive(Clone)]
pub enum Kind {
    /// The existing reference the symbolic HEAD points to.
    ///
    /// This is the common case.
    Symbolic(gix_ref::Reference),
    /// The yet-to-be-created reference the symbolic HEAD refers to.
    ///
    /// This is the case in a newly initialized repository.
    Unborn(gix_ref::FullName),
    /// The head points to an object directly, not to a symbolic reference.
    ///
    /// This state is less common and can occur when checking out commits directly.
    Detached {
        /// The object to which the head points to
        target: ObjectId,
        /// Possibly the final destination of `target` after following the object chain from tag objects to commits.
        peeled: Option<ObjectId>,
    },
}

impl Kind {
    /// Attach this instance to a `repo` to produce a [`Head`].
    pub fn attach(self, repo: &crate::Repository) -> Head<'_> {
        Head { kind: self, repo }
    }
}

/// Access
impl<'repo> Head<'repo> {
    /// Returns the name of this references, always `HEAD`.
    pub fn name(&self) -> &'static FullNameRef {
        // TODO: use a statically checked version of this when available.
        "HEAD".try_into().expect("HEAD is valid")
    }

    /// Returns the full reference name of this head if it is not detached, or `None` otherwise.
    pub fn referent_name(&self) -> Option<&FullNameRef> {
        Some(match &self.kind {
            Kind::Symbolic(r) => r.name.as_ref(),
            Kind::Unborn(name) => name.as_ref(),
            Kind::Detached { .. } => return None,
        })
    }

    /// Returns true if this instance is detached, and points to an object directly.
    pub fn is_detached(&self) -> bool {
        matches!(self.kind, Kind::Detached { .. })
    }

    /// Returns true if this instance is not yet born, hence it points to a ref that doesn't exist yet.
    ///
    /// This is the case in a newly initialized repository.
    pub fn is_unborn(&self) -> bool {
        matches!(self.kind, Kind::Unborn(_))
    }

    // TODO: tests
    /// Returns the id the head points to, which isn't possible on unborn heads.
    pub fn id(&self) -> Option<crate::Id<'repo>> {
        match &self.kind {
            Kind::Symbolic(r) => r.target.try_id().map(|oid| oid.to_owned().attach(self.repo)),
            Kind::Detached { peeled, target } => {
                (*peeled).unwrap_or_else(|| target.to_owned()).attach(self.repo).into()
            }
            Kind::Unborn(_) => None,
        }
    }

    /// Try to transform this instance into the symbolic reference that it points to, or return `None` if head is detached or unborn.
    pub fn try_into_referent(self) -> Option<crate::Reference<'repo>> {
        match self.kind {
            Kind::Symbolic(r) => r.attach(self.repo).into(),
            _ => None,
        }
    }
}

mod remote {
    use super::Head;
    use crate::{remote, Remote};

    /// Remote
    impl<'repo> Head<'repo> {
        /// Return the remote with which the currently checked our reference can be handled as configured by `branch.<name>.remote|pushRemote`
        /// or fall back to the non-branch specific remote configuration. `None` is returned if the head is detached or unborn, so there is
        /// no branch specific remote.
        ///
        /// This is equivalent to calling [`Reference::remote(…)`][crate::Reference::remote()] and
        /// [`Repository::remote_default_name()`][crate::Repository::remote_default_name()] in order.
        ///
        /// Combine it with [`find_default_remote()`][crate::Repository::find_default_remote()] as fallback to handle detached heads,
        /// i.e. obtain a remote even in case of detached heads.
        pub fn into_remote(
            self,
            direction: remote::Direction,
        ) -> Option<Result<Remote<'repo>, remote::find::existing::Error>> {
            let repo = self.repo;
            self.try_into_referent()?
                .remote(direction)
                .or_else(|| repo.find_default_remote(direction))
        }
    }
}

///
pub mod log;

///
pub mod peel;