Skip to main content

git_lfs/
man.rs

1//! Per-subcommand documentation extras (man pages + mdbook).
2//!
3//! The clap derive in [`crate::args`] is the source of truth for the
4//! NAME / SYNOPSIS / OPTIONS surface — xtask renders that automatically
5//! into both groff (man pages) and markdown (mdbook). This module owns
6//! everything richer: DESCRIPTION prose, EXAMPLES, NOTES, FILES,
7//! SEE ALSO.
8//!
9//! **Bodies are authored in markdown.** xtask passes them through
10//! verbatim for the markdown output and converts them to groff for the
11//! man pages, so a single source feeds both formats. The supported
12//! markdown vocabulary is intentionally small — paragraphs, bold/italic,
13//! code spans / fenced code blocks, bulleted and numbered lists. Stick
14//! to that and the groff conversion stays predictable.
15//!
16//! Each subcommand exposes its extras here as a [`ManContent`] entry in
17//! [`extras_for`]. Bodies live under `cli/man/<sub>/*.md` and are pulled
18//! in via [`include_str!`], keeping prose out of `man.rs`.
19//!
20//! Onboarding a new section is two-step:
21//! 1. Drop one or more `.md` files into `cli/man/<subcommand>/`.
22//! 2. Add a match arm in [`extras_for`] referencing them.
23//!
24//! Subcommands without an entry get the auto-generated page with no
25//! extras — still useful, just shorter.
26
27/// Hand-authored extras for a single command's documentation. Returned
28/// by [`extras_for`] keyed on the subcommand name (or `""` for the top-
29/// level `git-lfs` page). Both fields are markdown — xtask renders them
30/// to either groff or markdown depending on output format.
31#[derive(Debug)]
32pub struct ManContent {
33    /// Replaces the auto-generated DESCRIPTION (which is just the short
34    /// `about` from the clap derive). Markdown.
35    pub description: Option<&'static str>,
36
37    /// Sections appended after OPTIONS, in order. Each entry is
38    /// `(title, markdown body)`. Conventional titles: `EXAMPLES`,
39    /// `FILES`, `ENVIRONMENT`, `NOTES`, `BUGS`, `SEE ALSO`. The title
40    /// becomes a `.SH` in groff and a top-level `##` in markdown.
41    pub extra_sections: &'static [(&'static str, &'static str)],
42}
43
44impl ManContent {
45    pub const fn empty() -> Self {
46        Self {
47            description: None,
48            extra_sections: &[],
49        }
50    }
51}
52
53const EMPTY: ManContent = ManContent::empty();
54
55const ROOT: ManContent = ManContent {
56    description: None,
57    extra_sections: &[("EXAMPLES", include_str!("../man/root/examples.md"))],
58};
59
60/// Markdown body for the `REPORTING BUGS` section that xtask appends
61/// to every generated man / mdbook page. Single source of truth for
62/// the project URL and the "this is the Rust implementation" framing
63/// — change here and every page picks it up on the next regen.
64pub const REPORTING_BUGS: &str = include_str!("../man/reporting_bugs.md");
65
66const SMUDGE: ManContent = ManContent {
67    description: None,
68    extra_sections: &[
69        ("ENVIRONMENT", include_str!("../man/smudge/environment.md")),
70        ("KNOWN BUGS", include_str!("../man/smudge/known_bugs.md")),
71        ("SEE ALSO", include_str!("../man/smudge/see_also.md")),
72    ],
73};
74
75const CHECKOUT: ManContent = ManContent {
76    description: None,
77    extra_sections: &[("EXAMPLES", include_str!("../man/checkout/examples.md"))],
78};
79
80const FETCH: ManContent = ManContent {
81    description: None,
82    extra_sections: &[
83        (
84            "INCLUDE AND EXCLUDE",
85            include_str!("../man/fetch/include_and_exclude.md"),
86        ),
87        (
88            "DEFAULT REMOTE",
89            include_str!("../man/fetch/default_remote.md"),
90        ),
91        ("DEFAULT REFS", include_str!("../man/fetch/default_refs.md")),
92        (
93            "RECENT CHANGES",
94            include_str!("../man/fetch/recent_changes.md"),
95        ),
96        ("EXAMPLES", include_str!("../man/fetch/examples.md")),
97        ("SEE ALSO", include_str!("../man/fetch/see_also.md")),
98    ],
99};
100
101const PULL: ManContent = ManContent {
102    description: None,
103    extra_sections: &[
104        (
105            "INCLUDE AND EXCLUDE",
106            include_str!("../man/pull/include_and_exclude.md"),
107        ),
108        (
109            "DEFAULT REMOTE",
110            include_str!("../man/pull/default_remote.md"),
111        ),
112        ("EXAMPLES", include_str!("../man/pull/examples.md")),
113        ("SEE ALSO", include_str!("../man/pull/see_also.md")),
114    ],
115};
116
117const PUSH: ManContent = ManContent {
118    description: None,
119    extra_sections: &[("SEE ALSO", include_str!("../man/push/see_also.md"))],
120};
121
122const INSTALL: ManContent = ManContent {
123    description: None,
124    extra_sections: &[("SEE ALSO", include_str!("../man/install/see_also.md"))],
125};
126
127const UNINSTALL: ManContent = ManContent {
128    description: None,
129    extra_sections: &[("SEE ALSO", include_str!("../man/uninstall/see_also.md"))],
130};
131
132const TRACK: ManContent = ManContent {
133    description: None,
134    extra_sections: &[
135        ("EXAMPLES", include_str!("../man/track/examples.md")),
136        ("SEE ALSO", include_str!("../man/track/see_also.md")),
137    ],
138};
139
140const UNTRACK: ManContent = ManContent {
141    description: None,
142    extra_sections: &[
143        ("EXAMPLES", include_str!("../man/untrack/examples.md")),
144        ("SEE ALSO", include_str!("../man/untrack/see_also.md")),
145    ],
146};
147
148const LOCK: ManContent = ManContent {
149    description: None,
150    extra_sections: &[("SEE ALSO", include_str!("../man/lock/see_also.md"))],
151};
152
153const LOCKS: ManContent = ManContent {
154    description: None,
155    extra_sections: &[("SEE ALSO", include_str!("../man/locks/see_also.md"))],
156};
157
158const UNLOCK: ManContent = ManContent {
159    description: None,
160    extra_sections: &[("SEE ALSO", include_str!("../man/unlock/see_also.md"))],
161};
162
163const STATUS: ManContent = ManContent {
164    description: None,
165    extra_sections: &[("SEE ALSO", include_str!("../man/status/see_also.md"))],
166};
167
168const LS_FILES: ManContent = ManContent {
169    description: None,
170    extra_sections: &[("SEE ALSO", include_str!("../man/ls-files/see_also.md"))],
171};
172
173const PRUNE: ManContent = ManContent {
174    description: Some(include_str!("../man/prune/description.md")),
175    extra_sections: &[
176        ("RECENT FILES", include_str!("../man/prune/recent_files.md")),
177        (
178            "UNPUSHED LFS FILES",
179            include_str!("../man/prune/unpushed.md"),
180        ),
181        (
182            "VERIFY REMOTE",
183            include_str!("../man/prune/verify_remote.md"),
184        ),
185        (
186            "DEFAULT REMOTE",
187            include_str!("../man/prune/default_remote.md"),
188        ),
189        ("SEE ALSO", include_str!("../man/prune/see_also.md")),
190    ],
191};
192
193const FSCK: ManContent = ManContent {
194    description: None,
195    extra_sections: &[("SEE ALSO", include_str!("../man/fsck/see_also.md"))],
196};
197
198const CLEAN: ManContent = ManContent {
199    description: None,
200    extra_sections: &[("SEE ALSO", include_str!("../man/clean/see_also.md"))],
201};
202
203const FILTER_PROCESS: ManContent = ManContent {
204    description: None,
205    extra_sections: &[(
206        "SEE ALSO",
207        include_str!("../man/filter-process/see_also.md"),
208    )],
209};
210
211const CLONE: ManContent = ManContent {
212    description: None,
213    extra_sections: &[("SEE ALSO", include_str!("../man/clone/see_also.md"))],
214};
215
216const PRE_PUSH: ManContent = ManContent {
217    description: None,
218    extra_sections: &[("SEE ALSO", include_str!("../man/pre-push/see_also.md"))],
219};
220
221const POST_CHECKOUT: ManContent = ManContent {
222    description: None,
223    extra_sections: &[("SEE ALSO", include_str!("../man/post-checkout/see_also.md"))],
224};
225
226const POST_COMMIT: ManContent = ManContent {
227    description: None,
228    extra_sections: &[("SEE ALSO", include_str!("../man/post-commit/see_also.md"))],
229};
230
231const POST_MERGE: ManContent = ManContent {
232    description: None,
233    extra_sections: &[("SEE ALSO", include_str!("../man/post-merge/see_also.md"))],
234};
235
236const MIGRATE: ManContent = ManContent {
237    description: None,
238    extra_sections: &[
239        (
240            "INCLUDE AND EXCLUDE",
241            include_str!("../man/migrate/include_and_exclude.md"),
242        ),
243        (
244            "INCLUDE AND EXCLUDE REFERENCES",
245            include_str!("../man/migrate/include_and_exclude_references.md"),
246        ),
247        ("EXAMPLES", include_str!("../man/migrate/examples.md")),
248        ("SEE ALSO", include_str!("../man/migrate/see_also.md")),
249    ],
250};
251
252const MIGRATE_INFO: ManContent = ManContent {
253    description: None,
254    extra_sections: &[
255        ("EXAMPLES", include_str!("../man/migrate-info/examples.md")),
256        ("SEE ALSO", include_str!("../man/migrate-info/see_also.md")),
257    ],
258};
259
260const MIGRATE_IMPORT: ManContent = ManContent {
261    description: None,
262    extra_sections: &[
263        (
264            "EXAMPLES",
265            include_str!("../man/migrate-import/examples.md"),
266        ),
267        (
268            "SEE ALSO",
269            include_str!("../man/migrate-import/see_also.md"),
270        ),
271    ],
272};
273
274const MIGRATE_EXPORT: ManContent = ManContent {
275    description: None,
276    extra_sections: &[
277        (
278            "EXAMPLES",
279            include_str!("../man/migrate-export/examples.md"),
280        ),
281        (
282            "SEE ALSO",
283            include_str!("../man/migrate-export/see_also.md"),
284        ),
285    ],
286};
287
288const EXT: ManContent = ManContent {
289    description: None,
290    extra_sections: &[("EXAMPLES", include_str!("../man/ext/examples.md"))],
291};
292
293/// git-lfs-config(5) is a synthetic page — no clap-derived OPTIONS or
294/// DESCRIPTION. Everything lives in the extras. The OPTIONS section
295/// is built up across several commits; the framing sections
296/// (CONFIGURATION FILES / LFSCONFIG / EXAMPLES / SEE ALSO) bracket
297/// whatever lands in between.
298const CONFIG: ManContent = ManContent {
299    description: None,
300    extra_sections: &[
301        (
302            "CONFIGURATION FILES",
303            include_str!("../man/config/configuration_files.md"),
304        ),
305        (
306            "GENERAL SETTINGS",
307            include_str!("../man/config/general_settings.md"),
308        ),
309        (
310            "UPLOAD AND DOWNLOAD TRANSFER SETTINGS",
311            include_str!("../man/config/transfer_settings.md"),
312        ),
313        (
314            "PUSH SETTINGS",
315            include_str!("../man/config/push_settings.md"),
316        ),
317        (
318            "FETCH SETTINGS",
319            include_str!("../man/config/fetch_settings.md"),
320        ),
321        (
322            "PRUNE SETTINGS",
323            include_str!("../man/config/prune_settings.md"),
324        ),
325        ("EXTENSIONS", include_str!("../man/config/extensions.md")),
326        (
327            "OTHER SETTINGS",
328            include_str!("../man/config/other_settings.md"),
329        ),
330        ("LFSCONFIG", include_str!("../man/config/lfsconfig.md")),
331        ("EXAMPLES", include_str!("../man/config/examples.md")),
332        ("SEE ALSO", include_str!("../man/config/see_also.md")),
333    ],
334};
335
336/// Look up the doc extras for `subcommand` (e.g. `"fetch"`,
337/// `"checkout"`). Pass `""` for the top-level `git-lfs` page.
338/// Returns a reference to [`ManContent::empty`] when there's no entry,
339/// so the caller can always splice unconditionally.
340pub fn extras_for(subcommand: &str) -> &'static ManContent {
341    match subcommand {
342        "smudge" => &SMUDGE,
343        "ext" => &EXT,
344        "config" => &CONFIG,
345        "checkout" => &CHECKOUT,
346        "fetch" => &FETCH,
347        "pull" => &PULL,
348        "push" => &PUSH,
349        "install" => &INSTALL,
350        "uninstall" => &UNINSTALL,
351        "track" => &TRACK,
352        "untrack" => &UNTRACK,
353        "lock" => &LOCK,
354        "locks" => &LOCKS,
355        "unlock" => &UNLOCK,
356        "status" => &STATUS,
357        "ls-files" => &LS_FILES,
358        "prune" => &PRUNE,
359        "fsck" => &FSCK,
360        "clean" => &CLEAN,
361        "filter-process" => &FILTER_PROCESS,
362        "clone" => &CLONE,
363        "pre-push" => &PRE_PUSH,
364        "post-checkout" => &POST_CHECKOUT,
365        "post-commit" => &POST_COMMIT,
366        "post-merge" => &POST_MERGE,
367        "migrate" => &MIGRATE,
368        "migrate-info" => &MIGRATE_INFO,
369        "migrate-import" => &MIGRATE_IMPORT,
370        "migrate-export" => &MIGRATE_EXPORT,
371        "" => &ROOT,
372        _ => &EMPTY,
373    }
374}