Skip to content

Commit 3773b92

Browse files
committed
Merge branch 'filter-refs'
2 parents fd14489 + 461ff27 commit 3773b92

File tree

39 files changed

+623
-300
lines changed

39 files changed

+623
-300
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Please see _'Development Status'_ for a listing of all crates and their capabili
4848
* [x] **previous-branches** - list all previously checked out branches, powered by the ref-log.
4949
* **remote**
5050
* [x] **refs** - list all references available on the remote based on the current remote configuration.
51+
* [x] **ref-map** - show how remote references relate to their local tracking branches as mapped by refspecs.
5152
* **credential**
5253
* [x] **fill/approve/reject** - The same as `git credential`, but implemented in Rust, calling helpers only when from trusted configuration.
5354
* **free** - no git repository necessary
@@ -142,6 +143,7 @@ is usable to some extend.
142143
* [git-revision](https://github.com/Byron/gitoxide/blob/main/crate-status.md#git-revision)
143144
* [git-command](https://github.com/Byron/gitoxide/blob/main/crate-status.md#git-command)
144145
* [git-prompt](https://github.com/Byron/gitoxide/blob/main/crate-status.md#git-prompt)
146+
* [git-refspec](https://github.com/Byron/gitoxide/blob/main/crate-status.md#git-refspec)
145147
* `gitoxide-core`
146148
* **very early** _(possibly without any documentation and many rough edges)_
147149
* [git-worktree](https://github.com/Byron/gitoxide/blob/main/crate-status.md#git-worktree)

crate-status.md

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
### git-object
1515
* *decode (zero-copy)* borrowed objects
1616
* [x] commit
17-
* [x] parse the title, body, and provide a title summary.
1817
* [ ] parse [trailers](https://git-scm.com/docs/git-interpret-trailers#_description)
1918
* [x] tree
2019
* encode owned objects

git-config/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ unicode-bom = "1.1.4"
2929
bstr = { version = "1.0.1", default-features = false, features = ["std"] }
3030
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]}
3131
smallvec = "1.9.0"
32+
once_cell = "1.14.0"
3233

3334
document-features = { version = "0.2.0", optional = true }
3435

git-config/src/source.rs

+62-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use crate::Source;
99
/// The category of a [`Source`], in order of ascending precedence.
1010
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
1111
pub enum Kind {
12+
/// A special configuration file that ships with the git installation, and is thus tied to the used git binary.
13+
GitInstallation,
1214
/// A source shared for the entire system.
1315
System,
1416
/// Application specific configuration unique for each user of the `System`.
@@ -23,7 +25,8 @@ impl Kind {
2325
/// Return a list of sources associated with this `Kind` of source, in order of ascending precedence.
2426
pub fn sources(self) -> &'static [Source] {
2527
let src = match self {
26-
Kind::System => &[Source::System] as &[_],
28+
Kind::GitInstallation => &[Source::GitInstallation] as &[_],
29+
Kind::System => &[Source::System],
2730
Kind::Global => &[Source::Git, Source::User],
2831
Kind::Repository => &[Source::Local, Source::Worktree],
2932
Kind::Override => &[Source::Env, Source::Cli, Source::Api],
@@ -41,6 +44,7 @@ impl Source {
4144
pub const fn kind(self) -> Kind {
4245
use Source::*;
4346
match self {
47+
GitInstallation => Kind::GitInstallation,
4448
System => Kind::System,
4549
Git | User => Kind::Global,
4650
Local | Worktree => Kind::Repository,
@@ -61,6 +65,7 @@ impl Source {
6165
pub fn storage_location(self, env_var: &mut dyn FnMut(&str) -> Option<OsString>) -> Option<Cow<'static, Path>> {
6266
use Source::*;
6367
match self {
68+
GitInstallation => git::install_config_path().map(git_path::from_bstr),
6469
System => env_var("GIT_CONFIG_NO_SYSTEM")
6570
.is_none()
6671
.then(|| PathBuf::from(env_var("GIT_CONFIG_SYSTEM").unwrap_or_else(|| "/etc/gitconfig".into())).into()),
@@ -99,3 +104,59 @@ impl Source {
99104
}
100105
}
101106
}
107+
108+
/// Environment information involving the `git` program itself.
109+
mod git {
110+
use bstr::{BStr, BString, ByteSlice};
111+
use std::process::{Command, Stdio};
112+
113+
/// Returns the file that contains git configuration coming with the installation of the `git` file in the current `PATH`, or `None`
114+
/// if no `git` executable was found or there were other errors during execution.
115+
pub fn install_config_path() -> Option<&'static BStr> {
116+
static PATH: once_cell::sync::Lazy<Option<BString>> = once_cell::sync::Lazy::new(|| {
117+
let mut cmd = Command::new(if cfg!(windows) { "git.exe" } else { "git" });
118+
cmd.args(["config", "-l", "--show-origin"])
119+
.stdin(Stdio::null())
120+
.stderr(Stdio::null());
121+
first_file_from_config_with_origin(cmd.output().ok()?.stdout.as_slice().into()).map(ToOwned::to_owned)
122+
});
123+
PATH.as_ref().map(|b| b.as_ref())
124+
}
125+
126+
fn first_file_from_config_with_origin(source: &BStr) -> Option<&BStr> {
127+
let file = source.strip_prefix(b"file:")?;
128+
let end_pos = file.find_byte(b'\t')?;
129+
file[..end_pos].as_bstr().into()
130+
}
131+
132+
#[cfg(test)]
133+
mod tests {
134+
#[test]
135+
fn first_file_from_config_with_origin() {
136+
let macos = "file:/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig credential.helper=osxkeychain\nfile:/Users/byron/.gitconfig push.default=simple\n";
137+
let win_msys =
138+
"file:C:/git-sdk-64/etc/gitconfig core.symlinks=false\r\nfile:C:/git-sdk-64/etc/gitconfig core.autocrlf=true";
139+
let win_cmd = "file:C:/Program Files/Git/etc/gitconfig diff.astextplain.textconv=astextplain\r\nfile:C:/Program Files/Git/etc/gitconfig filter.lfs.clean=git-lfs clean -- %f\r\n";
140+
let linux = "file:/home/parallels/.gitconfig core.excludesfile=~/.gitignore\n";
141+
let bogus = "something unexpected";
142+
let empty = "";
143+
144+
for (source, expected) in [
145+
(
146+
macos,
147+
Some("/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig"),
148+
),
149+
(win_msys, Some("C:/git-sdk-64/etc/gitconfig")),
150+
(win_cmd, Some("C:/Program Files/Git/etc/gitconfig")),
151+
(linux, Some("/home/parallels/.gitconfig")),
152+
(bogus, None),
153+
(empty, None),
154+
] {
155+
assert_eq!(
156+
super::first_file_from_config_with_origin(source.into()),
157+
expected.map(Into::into)
158+
);
159+
}
160+
}
161+
}
162+
}

git-config/src/types.rs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use crate::{
1515
/// their source.
1616
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
1717
pub enum Source {
18+
/// A special configuration file that ships with the git installation, and is thus tied to the used git binary.
19+
GitInstallation,
1820
/// System-wide configuration path. This is defined as
1921
/// `$(prefix)/etc/gitconfig` (where prefix is the git-installation directory).
2022
System,

git-protocol/src/fetch/handshake.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use git_transport::client::Capabilities;
33
use crate::fetch::Ref;
44

55
/// The result of the [`handshake()`][super::handshake()] function.
6+
#[derive(Debug, Clone)]
7+
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
68
pub struct Outcome {
79
/// The protocol version the server responded with. It might have downgraded the desired version.
810
pub server_protocol_version: git_transport::Protocol,

git-protocol/src/fetch/refs/mod.rs

+20-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bstr::BString;
1+
use bstr::{BStr, BString};
22

33
mod error {
44
use crate::fetch::refs::parse;
@@ -50,24 +50,24 @@ pub mod parse {
5050
pub enum Ref {
5151
/// A ref pointing to a `tag` object, which in turns points to an `object`, usually a commit
5252
Peeled {
53-
/// The path at which the ref is located, like `/refs/heads/main`.
54-
path: BString,
53+
/// The name at which the ref is located, like `refs/heads/main`.
54+
full_ref_name: BString,
5555
/// The hash of the tag the ref points to.
5656
tag: git_hash::ObjectId,
5757
/// The hash of the object the `tag` points to.
5858
object: git_hash::ObjectId,
5959
},
6060
/// A ref pointing to a commit object
6161
Direct {
62-
/// The path at which the ref is located, like `/refs/heads/main`.
63-
path: BString,
62+
/// The name at which the ref is located, like `refs/heads/main`.
63+
full_ref_name: BString,
6464
/// The hash of the object the ref points to.
6565
object: git_hash::ObjectId,
6666
},
6767
/// A symbolic ref pointing to `target` ref, which in turn points to an `object`
6868
Symbolic {
69-
/// The path at which the symbolic ref is located, like `/refs/heads/main`.
70-
path: BString,
69+
/// The name at which the symbolic ref is located, like `refs/heads/main`.
70+
full_ref_name: BString,
7171
/// The path of the ref the symbolic ref points to, see issue [#205] for details
7272
///
7373
/// [#205]: https://github.com/Byron/gitoxide/issues/205
@@ -78,13 +78,20 @@ pub enum Ref {
7878
}
7979

8080
impl Ref {
81-
/// Provide shared fields referring to the ref itself, namely `(path, object id)`.
82-
/// In case of peeled refs, the tag object itself is returned as it is what the path refers to.
83-
pub fn unpack(&self) -> (&BString, &git_hash::ObjectId) {
81+
/// Provide shared fields referring to the ref itself, namely `(name, target, [peeled])`.
82+
/// In case of peeled refs, the tag object itself is returned as it is what the ref directly refers to, and target of the tag is returned
83+
/// as `peeled`.
84+
pub fn unpack(&self) -> (&BStr, &git_hash::oid, Option<&git_hash::oid>) {
8485
match self {
85-
Ref::Direct { path, object, .. }
86-
| Ref::Peeled { path, tag: object, .. } // the tag acts as reference
87-
| Ref::Symbolic { path, object, .. } => (path, object),
86+
Ref::Direct { full_ref_name, object }
87+
| Ref::Symbolic {
88+
full_ref_name, object, ..
89+
} => (full_ref_name.as_ref(), object, None),
90+
Ref::Peeled {
91+
full_ref_name,
92+
tag: object,
93+
object: peeled,
94+
} => (full_ref_name.as_ref(), object, Some(peeled)),
8895
}
8996
}
9097
}

git-protocol/src/fetch/refs/shared.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,28 @@ impl From<InternalRef> for Ref {
99
path,
1010
target: Some(target),
1111
object,
12-
} => Ref::Symbolic { path, target, object },
12+
} => Ref::Symbolic {
13+
full_ref_name: path,
14+
target,
15+
object,
16+
},
1317
InternalRef::Symbolic {
1418
path,
1519
target: None,
1620
object,
17-
} => Ref::Direct { path, object },
18-
InternalRef::Peeled { path, tag, object } => Ref::Peeled { path, tag, object },
19-
InternalRef::Direct { path, object } => Ref::Direct { path, object },
21+
} => Ref::Direct {
22+
full_ref_name: path,
23+
object,
24+
},
25+
InternalRef::Peeled { path, tag, object } => Ref::Peeled {
26+
full_ref_name: path,
27+
tag,
28+
object,
29+
},
30+
InternalRef::Direct { path, object } => Ref::Direct {
31+
full_ref_name: path,
32+
object,
33+
},
2034
InternalRef::SymbolicForLookup { .. } => {
2135
unreachable!("this case should have been removed during processing")
2236
}
@@ -170,17 +184,17 @@ pub(in crate::fetch::refs) fn parse_v2(line: &str) -> Result<Ref, Error> {
170184
}
171185
match attribute {
172186
"peeled" => Ref::Peeled {
173-
path: path.into(),
187+
full_ref_name: path.into(),
174188
object: git_hash::ObjectId::from_hex(value.as_bytes())?,
175189
tag: id,
176190
},
177191
"symref-target" => match value {
178192
"(null)" => Ref::Direct {
179-
path: path.into(),
193+
full_ref_name: path.into(),
180194
object: id,
181195
},
182196
name => Ref::Symbolic {
183-
path: path.into(),
197+
full_ref_name: path.into(),
184198
object: id,
185199
target: name.into(),
186200
},
@@ -198,7 +212,7 @@ pub(in crate::fetch::refs) fn parse_v2(line: &str) -> Result<Ref, Error> {
198212
} else {
199213
Ref::Direct {
200214
object: id,
201-
path: path.into(),
215+
full_ref_name: path.into(),
202216
}
203217
})
204218
}

git-protocol/src/fetch/tests/refs.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,25 @@ async fn extract_references_from_v2_refs() {
1919
out,
2020
vec![
2121
Ref::Symbolic {
22-
path: "HEAD".into(),
22+
full_ref_name: "HEAD".into(),
2323
target: "refs/heads/main".into(),
2424
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
2525
},
2626
Ref::Direct {
27-
path: "MISSING_NAMESPACE_TARGET".into(),
27+
full_ref_name: "MISSING_NAMESPACE_TARGET".into(),
2828
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
2929
},
3030
Ref::Direct {
31-
path: "refs/heads/main".into(),
31+
full_ref_name: "refs/heads/main".into(),
3232
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
3333
},
3434
Ref::Peeled {
35-
path: "refs/tags/foo".into(),
35+
full_ref_name: "refs/tags/foo".into(),
3636
tag: oid("7fe1b98b39423b71e14217aa299a03b7c937d656"),
3737
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
3838
},
3939
Ref::Direct {
40-
path: "refs/tags/blaz".into(),
40+
full_ref_name: "refs/tags/blaz".into(),
4141
object: oid("7fe1b98b39423b71e14217aa299a03b7c937d6ff")
4242
},
4343
]
@@ -66,24 +66,24 @@ dce0ea858eef7ff61ad345cc5cdac62203fb3c10 refs/tags/git-commitgraph-v0.0.0
6666
out,
6767
vec![
6868
Ref::Symbolic {
69-
path: "HEAD".into(),
69+
full_ref_name: "HEAD".into(),
7070
target: "refs/heads/main".into(),
7171
object: oid("73a6868963993a3328e7d8fe94e5a6ac5078a944")
7272
},
7373
Ref::Direct {
74-
path: "MISSING_NAMESPACE_TARGET".into(),
74+
full_ref_name: "MISSING_NAMESPACE_TARGET".into(),
7575
object: oid("21c9b7500cb144b3169a6537961ec2b9e865be81")
7676
},
7777
Ref::Direct {
78-
path: "refs/heads/main".into(),
78+
full_ref_name: "refs/heads/main".into(),
7979
object: oid("73a6868963993a3328e7d8fe94e5a6ac5078a944")
8080
},
8181
Ref::Direct {
82-
path: "refs/pull/13/head".into(),
82+
full_ref_name: "refs/pull/13/head".into(),
8383
object: oid("8e472f9ccc7d745927426cbb2d9d077de545aa4e")
8484
},
8585
Ref::Peeled {
86-
path: "refs/tags/git-commitgraph-v0.0.0".into(),
86+
full_ref_name: "refs/tags/git-commitgraph-v0.0.0".into(),
8787
tag: oid("dce0ea858eef7ff61ad345cc5cdac62203fb3c10"),
8888
object: oid("21c9b7500cb144b3169a6537961ec2b9e865be81")
8989
},

git-protocol/tests/fetch/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ mod blocking_io {
169169
) -> io::Result<()> {
170170
for wanted in response.wanted_refs() {
171171
self.wanted_refs.push(fetch::Ref::Direct {
172-
path: wanted.path.clone(),
172+
full_ref_name: wanted.path.clone(),
173173
object: wanted.id,
174174
});
175175
}
@@ -230,7 +230,7 @@ mod async_io {
230230
) -> io::Result<()> {
231231
for wanted in response.wanted_refs() {
232232
self.wanted_refs.push(fetch::Ref::Direct {
233-
path: wanted.path.clone(),
233+
full_ref_name: wanted.path.clone(),
234234
object: wanted.id,
235235
});
236236
}

git-protocol/tests/fetch/v1.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ async fn ls_remote() -> crate::Result {
4949
delegate.refs,
5050
vec![
5151
fetch::Ref::Symbolic {
52-
path: "HEAD".into(),
52+
full_ref_name: "HEAD".into(),
5353
object: oid("808e50d724f604f69ab93c6da2919c014667bedb"),
5454
target: "refs/heads/master".into()
5555
},
5656
fetch::Ref::Direct {
57-
path: "refs/heads/master".into(),
57+
full_ref_name: "refs/heads/master".into(),
5858
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
5959
}
6060
]

git-protocol/tests/fetch/v2.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ async fn ls_remote() -> crate::Result {
7575
delegate.refs,
7676
vec![
7777
fetch::Ref::Symbolic {
78-
path: "HEAD".into(),
78+
full_ref_name: "HEAD".into(),
7979
object: oid("808e50d724f604f69ab93c6da2919c014667bedb"),
8080
target: "refs/heads/master".into()
8181
},
8282
fetch::Ref::Direct {
83-
path: "refs/heads/master".into(),
83+
full_ref_name: "refs/heads/master".into(),
8484
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
8585
}
8686
]
@@ -167,7 +167,7 @@ async fn ref_in_want() -> crate::Result {
167167
assert_eq!(
168168
delegate.wanted_refs,
169169
vec![fetch::Ref::Direct {
170-
path: "refs/heads/main".into(),
170+
full_ref_name: "refs/heads/main".into(),
171171
object: oid("9e320b9180e0b5580af68fa3255b7f3d9ecd5af0"),
172172
}]
173173
);

0 commit comments

Comments
 (0)