2
2
#![ deny( rust_2018_idioms, missing_docs) ]
3
3
#![ forbid( unsafe_code) ]
4
4
5
+ use bstr:: BString ;
5
6
use std:: ffi:: OsString ;
7
+ use std:: path:: PathBuf ;
6
8
7
9
/// A structure to keep settings to use when invoking a command via [`spawn()`][Prepare::spawn()], after creating it with [`prepare()`].
8
10
pub struct Prepare {
9
11
/// The command to invoke (either with or without shell depending on `use_shell`.
10
12
pub command : OsString ,
13
+ /// Additional information to be passed to the spawned command.
14
+ pub context : Option < Context > ,
11
15
/// The way standard input is configured.
12
16
pub stdin : std:: process:: Stdio ,
13
17
/// The way standard output is configured.
@@ -35,6 +39,37 @@ pub struct Prepare {
35
39
pub allow_manual_arg_splitting : bool ,
36
40
}
37
41
42
+ /// Additional information that is relevant to spawned processes, which typically receive
43
+ /// a wealth of contextual information when spawned from `git`.
44
+ ///
45
+ /// See [the git source code](https://github.com/git/git/blob/cfb8a6e9a93adbe81efca66e6110c9b4d2e57169/git.c#L191)
46
+ /// for details.
47
+ #[ derive( Debug , Default , Clone ) ]
48
+ pub struct Context {
49
+ /// The `.git` directory that contains the repository.
50
+ ///
51
+ /// If set, it will be used to set the the `GIT_DIR` environment variable.
52
+ pub git_dir : Option < PathBuf > ,
53
+ /// Set the `GIT_WORK_TREE` environment variable with the given path.
54
+ pub worktree_dir : Option < PathBuf > ,
55
+ /// If `true`, set `GIT_NO_REPLACE_OBJECTS` to `1`, which turns off object replacements, or `0` otherwise.
56
+ /// If `None`, the variable won't be set.
57
+ pub no_replace_objects : Option < bool > ,
58
+ /// Set the `GIT_NAMESPACE` variable with the given value, effectively namespacing all
59
+ /// operations on references.
60
+ pub ref_namespace : Option < BString > ,
61
+ /// If `true`, set `GIT_LITERAL_PATHSPECS` to `1`, which makes globs literal and prefixes as well, or `0` otherwise.
62
+ /// If `None`, the variable won't be set.
63
+ pub literal_pathspecs : Option < bool > ,
64
+ /// If `true`, set `GIT_GLOB_PATHSPECS` to `1`, which lets wildcards not match the `/` character, and equals the `:(glob)` prefix.
65
+ /// If `false`, set `GIT_NOGLOB_PATHSPECS` to `1` which lets globs match only themselves.
66
+ /// If `None`, the variable won't be set.
67
+ pub glob_pathspecs : Option < bool > ,
68
+ /// If `true`, set `GIT_ICASE_PATHSPECS` to `1`, to let patterns match case-insensitively, or `0` otherwise.
69
+ /// If `None`, the variable won't be set.
70
+ pub icase_pathspecs : Option < bool > ,
71
+ }
72
+
38
73
mod prepare {
39
74
use std:: {
40
75
ffi:: OsString ,
@@ -43,7 +78,7 @@ mod prepare {
43
78
44
79
use bstr:: ByteSlice ;
45
80
46
- use crate :: Prepare ;
81
+ use crate :: { Context , Prepare } ;
47
82
48
83
/// Builder
49
84
impl Prepare {
@@ -67,6 +102,15 @@ mod prepare {
67
102
self
68
103
}
69
104
105
+ /// Set additional `ctx` to be used when spawning the process.
106
+ ///
107
+ /// Note that this is a must for most kind of commands that `git` usually spawns,
108
+ /// as at least they need to know the correct `git` repository to function.
109
+ pub fn with_context ( mut self , ctx : Context ) -> Self {
110
+ self . context = Some ( ctx) ;
111
+ self
112
+ }
113
+
70
114
/// Use a shell, but try to split arguments by hand if this be safely done without a shell.
71
115
///
72
116
/// If that's not the case, use a shell instead.
@@ -164,6 +208,36 @@ mod prepare {
164
208
. stderr ( prep. stderr )
165
209
. envs ( prep. env )
166
210
. args ( prep. args ) ;
211
+ if let Some ( ctx) = prep. context {
212
+ if let Some ( git_dir) = ctx. git_dir {
213
+ cmd. env ( "GIT_DIR" , & git_dir) ;
214
+ }
215
+ if let Some ( worktree_dir) = ctx. worktree_dir {
216
+ cmd. env ( "GIT_WORK_TREE" , worktree_dir) ;
217
+ }
218
+ if let Some ( value) = ctx. no_replace_objects {
219
+ cmd. env ( "GIT_NO_REPLACE_OBJECTS" , usize:: from ( value) . to_string ( ) ) ;
220
+ }
221
+ if let Some ( namespace) = ctx. ref_namespace {
222
+ cmd. env ( "GIT_NAMESPACE" , gix_path:: from_bstring ( namespace) ) ;
223
+ }
224
+ if let Some ( value) = ctx. literal_pathspecs {
225
+ cmd. env ( "GIT_LITERAL_PATHSPECS" , usize:: from ( value) . to_string ( ) ) ;
226
+ }
227
+ if let Some ( value) = ctx. glob_pathspecs {
228
+ cmd. env (
229
+ if value {
230
+ "GIT_GLOB_PATHSPECS"
231
+ } else {
232
+ "GIT_NOGLOB_PATHSPECS"
233
+ } ,
234
+ "1" ,
235
+ ) ;
236
+ }
237
+ if let Some ( value) = ctx. icase_pathspecs {
238
+ cmd. env ( "GIT_ICASE_PATHSPECS" , usize:: from ( value) . to_string ( ) ) ;
239
+ }
240
+ }
167
241
cmd
168
242
}
169
243
}
@@ -176,9 +250,16 @@ mod prepare {
176
250
/// - `stdin` is null to prevent blocking unexpectedly on consumption of stdin
177
251
/// - `stdout` is captured for consumption by the caller
178
252
/// - `stderr` is inherited to allow the command to provide context to the user
253
+ ///
254
+ /// ### Warning
255
+ ///
256
+ /// When using this method, be sure that the invoked program doesn't rely on the current working dir and/or
257
+ /// environment variables to know its context. If so, call instead [`Prepare::with_context()`] to provide
258
+ /// additional information.
179
259
pub fn prepare ( cmd : impl Into < OsString > ) -> Prepare {
180
260
Prepare {
181
261
command : cmd. into ( ) ,
262
+ context : None ,
182
263
stdin : std:: process:: Stdio :: null ( ) ,
183
264
stdout : std:: process:: Stdio :: piped ( ) ,
184
265
stderr : std:: process:: Stdio :: inherit ( ) ,
0 commit comments