git_lfs/args.rs
1//! Command-line interface for `git-lfs`.
2//!
3//! This module implements the command-line interface for
4//! `git-lfs`. It uses `clap` to parse command-line arguments.
5//! Commands are split out into subcommands, with one struct per
6//! subcommand.
7//!
8//! This module is public so that other workspace tools can depend
9//! on it. Concretely, the man pages and markdown documentation are
10//! automatically generated from the command-line definitions.
11
12use std::path::PathBuf;
13
14use clap::{Args, Parser, Subcommand};
15
16/// Git LFS is a system for managing and versioning large files
17/// in association with a Git repository. Instead of storing the
18/// large files within the Git repository as blobs, Git LFS stores
19/// special "pointer files" in the repository, while storing the
20/// actual file contents on a Git LFS server. The contents of the
21/// large file are downloaded automatically when needed, for example
22/// when a Git branch containing the large file is checked out.
23///
24/// Git LFS works by using a "smudge" filter to look up the large
25/// file contents based on the pointer file, and a "clean" filter
26/// to create a new version of the pointer file when the large file's
27/// contents change. It also uses a pre-push hook to upload the large
28/// file contents to the Git LFS server whenever a commit containing
29/// a new large file version is about to be pushed to the
30/// corresponding Git server.
31#[derive(Parser)]
32#[command(
33 name = "git-lfs",
34 about = "Git LFS — large file storage for git",
35 // We want `git lfs --version` to print the same banner as
36 // `git lfs version`. clap's auto-derived `--version` would
37 // emit `git-lfs <version>` (one token, no `/` separator),
38 // which doesn't match the user-agent style upstream uses.
39 // Suppress clap's flag and handle --version ourselves.
40 disable_version_flag = true,
41 max_term_width = 100,
42)]
43pub struct Cli {
44 /// Print the version banner and exit.
45 #[arg(long, short = 'V', global = true)]
46 pub version: bool,
47
48 #[command(subcommand)]
49 pub command: Option<Command>,
50}
51
52// Subcommand convention: each subcommand is a tuple variant on `Command`
53// delegating to a `*Args` struct. The struct's rustdoc drives clap's help
54// output (first paragraph becomes `about`, the rest becomes `long_about`),
55// plus `#[command(...)]` extras like `after_help`, aliases, and arg-group
56// headings. Don't put rustdoc on the variant itself; it would shadow the
57// struct's docs in the generated help.
58#[derive(Subcommand)]
59pub enum Command {
60 Clean(CleanArgs),
61 Smudge(SmudgeArgs),
62 Install(InstallArgs),
63 Uninstall(UninstallArgs),
64 Track(TrackArgs),
65 Untrack(UntrackArgs),
66 FilterProcess(FilterProcessArgs),
67 Fetch(FetchArgs),
68 Pull(PullArgs),
69 Push(PushArgs),
70 Clone(CloneArgs),
71 PostCheckout(PostCheckoutArgs),
72 PostCommit(PostCommitArgs),
73 PostMerge(PostMergeArgs),
74 PrePush(PrePushArgs),
75 Version(VersionArgs),
76 Pointer(PointerArgs),
77 Env(EnvArgs),
78 Ext(ExtArgs),
79 Update(UpdateArgs),
80 Migrate(MigrateArgs),
81 Checkout(CheckoutArgs),
82 Prune(PruneArgs),
83 Fsck(FsckArgs),
84 Status(StatusArgs),
85 Lock(LockArgs),
86 Locks(LocksArgs),
87 Unlock(UnlockArgs),
88 LsFiles(LsFilesArgs),
89 Logs(LogsArgs),
90 MergeDriver(MergeDriverArgs),
91}
92
93/// Git clean filter that converts large files to pointers
94///
95/// Read the contents of a large file from standard input, and write a
96/// Git LFS pointer file for that file to standard output.
97///
98/// Clean is typically run by Git’s clean filter, configured by the
99/// repository’s Git attributes.
100///
101/// Clean is not part of the user-facing Git plumbing commands.
102/// To preview the pointer of a large file as it would be generated,
103/// see the git-lfs-pointer(1) command.
104#[derive(Args)]
105pub struct CleanArgs {
106 /// Working-tree path of the file being cleaned.
107 ///
108 /// Substituted for `%f` in any configured `lfs.extension.<name>.clean` command.
109 pub path: Option<PathBuf>,
110}
111
112/// Git smudge filter that converts pointer in blobs to the actual content
113///
114/// Read a Git LFS pointer file from standard input and write the contents of the
115/// corresponding large file to standard output. If needed, download the file’s
116/// contents from the Git LFS endpoint. The argument, if provided, is only used
117/// for a progress bar.
118///
119/// Smudge is typically run by Git’s smudge filter, configured by the repository’s
120/// Git attributes.
121///
122/// In your Git configuration or in a .lfsconfig file, you may set either or both
123/// of `lfs.fetchinclude` and `lfs.fetchexclude` to comma-separated lists of paths.
124/// If `lfs.fetchinclude` is defined, Git LFS pointer files will only be replaced
125/// with the contents of the corresponding Git LFS object file if their path
126/// matches one in that list, and if `lfs.fetchexclude` is defined, Git LFS pointer
127/// files will only be replaced with the contents of the corresponding Git LFS
128/// object file if their path does not match one in that list. Paths are matched
129/// using wildcard matching as per gitignore(5). Git LFS pointer files that are
130/// not replaced with the contents of their corresponding object files are simply
131/// copied to standard output without change.
132///
133/// Without any options, git lfs smudge outputs the raw Git LFS content to standard
134/// output.
135#[derive(Args)]
136pub struct SmudgeArgs {
137 /// Working-tree path of the file being smudged (currently unused).
138 pub path: Option<PathBuf>,
139 /// Skip automatic downloading of objects on clone or pull.
140 ///
141 /// Equivalent to `GIT_LFS_SKIP_SMUDGE=1`. Wired up by `git lfs install --skip-smudge`.
142 #[arg(long)]
143 pub skip: bool,
144}
145
146/// Install Git LFS configuration
147///
148/// Set up the `lfs` smudge and clean filters under the name `lfs` in
149/// the global Git config, and (when run from inside a repository)
150/// install a pre-push hook to run git-lfs-pre-push(1). If
151/// `core.hooksPath` is configured in any Git configuration (supported
152/// on Git v2.9.0 or later), the pre-push hook is installed to that
153/// directory instead.
154///
155/// Without any options, only sets up the `lfs` smudge and clean filters
156/// if they are not already set.
157#[derive(Args)]
158pub struct InstallArgs {
159 // TODO(post-1.0): replace the --local/--system/--worktree/--file mutex
160 // with a clap ArgGroup (multiple = false). Validation lives in
161 // resolve_install_scope (cli/src/main.rs); kept manual because
162 // tests/t-install.sh:329 (and the t-install-worktree / t-uninstall /
163 // t-uninstall-worktree variants) assert upstream's exact wording
164 // ("Only one of the --local, --system, --worktree, and --file
165 // options can be specified."). Worth taking once we're free to
166 // update those assertions.
167 /// Set the `lfs` smudge and clean filters, overwriting existing
168 /// values.
169 #[arg(short, long)]
170 pub force: bool,
171
172 /// Set the `lfs` smudge and clean filters in the local repository's
173 /// git config, instead of the global git config (`~/.gitconfig`).
174 #[arg(short, long)]
175 pub local: bool,
176
177 /// Set the `lfs` smudge and clean filters in the current working
178 /// tree's git config, instead of the global git config
179 /// (`~/.gitconfig`) or local repository's git config
180 /// (`$GIT_DIR/config`).
181 ///
182 /// If multiple working trees are in use, the Git config extension
183 /// `worktreeConfig` must be enabled to use this option. If only one
184 /// working tree is in use, `--worktree` has the same effect as
185 /// `--local`. Available only on Git v2.20.0 or later.
186 #[arg(short, long)]
187 pub worktree: bool,
188
189 /// Set the `lfs` smudge and clean filters in the system git config,
190 /// e.g. `/etc/gitconfig` instead of the global git config
191 /// (`~/.gitconfig`).
192 #[arg(long)]
193 pub system: bool,
194
195 /// Set the `lfs` smudge and clean filters in the Git configuration
196 /// file specified by `<PATH>`.
197 #[arg(long, value_name = "PATH")]
198 pub file: Option<PathBuf>,
199
200 /// Skip automatic downloading of objects on clone or pull.
201 ///
202 /// Requires a manual `git lfs pull` every time a new commit is
203 /// checked out on the repository.
204 #[arg(short, long)]
205 pub skip_smudge: bool,
206
207 /// Skip installation of hooks into the local repository.
208 ///
209 /// Use if you want to install the LFS filters but not make changes
210 /// to the hooks. Valid alongside `--local`, `--worktree`, `--system`,
211 /// or `--file`.
212 #[arg(long)]
213 pub skip_repo: bool,
214}
215
216/// Remove Git LFS configuration
217///
218/// Remove the `lfs` clean and smudge filters from the global Git config,
219/// and (when run from inside a Git repository) uninstall the Git LFS
220/// pre-push hook. Hooks that don't match what we would write are left
221/// untouched.
222#[derive(Args)]
223pub struct UninstallArgs {
224 // TODO(post-1.0): same --local/--system/--worktree/--file mutex as
225 // InstallArgs — share a clap ArgGroup. See InstallArgs's TODO for
226 // the rationale and test references.
227 /// Optional mode. With `hooks`, removes only the LFS git hooks and
228 /// leaves the filter config alone (the inverse of `--skip-repo`).
229 pub mode: Option<String>,
230
231 /// Remove the `lfs` smudge and clean filters from the local
232 /// repository's git config, instead of the global git config
233 /// (`~/.gitconfig`).
234 #[arg(short, long)]
235 pub local: bool,
236
237 /// Remove the `lfs` smudge and clean filters from the current
238 /// working tree's git config, instead of the global git config
239 /// (`~/.gitconfig`) or local repository's git config
240 /// (`$GIT_DIR/config`).
241 ///
242 /// If multiple working trees are in use, the Git config extension
243 /// `worktreeConfig` must be enabled to use this option. If only one
244 /// working tree is in use, `--worktree` has the same effect as
245 /// `--local`. Available only on Git v2.20.0 or later.
246 #[arg(short, long)]
247 pub worktree: bool,
248
249 /// Remove the `lfs` smudge and clean filters from the system git
250 /// config, instead of the global git config (`~/.gitconfig`).
251 #[arg(long)]
252 pub system: bool,
253
254 /// Remove the `lfs` smudge and clean filters from the Git
255 /// configuration file specified by `<PATH>`.
256 #[arg(long, value_name = "PATH")]
257 pub file: Option<PathBuf>,
258
259 /// Skip cleanup of the local repo.
260 ///
261 /// Use if you want to uninstall the global LFS filters but not
262 /// make changes to the current repo.
263 #[arg(long)]
264 pub skip_repo: bool,
265}
266
267/// View or add Git LFS paths to Git attributes
268///
269/// Start tracking the given pattern(s) through Git LFS. The argument is
270/// written to `.gitattributes`. If no paths are provided, list the
271/// currently-tracked paths.
272///
273/// Per gitattributes(5), patterns use the gitignore(5) pattern rules to
274/// match paths. This means that patterns containing asterisk (`*`),
275/// question mark (`?`), and the bracket characters (`[` and `]`) are
276/// treated specially; to disable this behavior and treat them literally
277/// instead, use `--filename` or escape the character with a backslash.
278#[derive(Args)]
279pub struct TrackArgs {
280 /// File patterns to track (e.g. `*.jpg`, `data/*.bin`).
281 pub patterns: Vec<String>,
282
283 /// Log files which `git lfs track` will touch. Disabled by default.
284 #[arg(short, long)]
285 pub verbose: bool,
286
287 /// Log all actions that would normally take place (adding entries
288 /// to `.gitattributes`, touching files on disk, etc.) without
289 /// performing any mutative operations.
290 ///
291 /// Implicitly mocks the behavior of `--verbose`, logging in greater
292 /// detail what it is doing. Disabled by default.
293 #[arg(short, long)]
294 pub dry_run: bool,
295
296 /// Write the currently tracked patterns as JSON to standard output.
297 ///
298 /// Intended for interoperation with external tools. Cannot be
299 /// combined with any pattern arguments. If `--no-excluded` is also
300 /// provided, that option will have no effect.
301 #[arg(short, long)]
302 pub json: bool,
303
304 /// Treat the arguments as literal filenames, not as patterns.
305 ///
306 /// Any special glob characters in the filename will be escaped
307 /// when writing the `.gitattributes` file.
308 #[arg(long)]
309 pub filename: bool,
310
311 /// Make the paths "lockable" — they should be locked to edit them,
312 /// and will be made read-only in the working copy when not locked.
313 #[arg(short, long)]
314 pub lockable: bool,
315
316 /// Remove the lockable flag from the paths so they are no longer
317 /// read-only unless locked.
318 #[arg(long)]
319 pub not_lockable: bool,
320
321 /// Don't list patterns that are excluded in the output; only list
322 /// patterns that are tracked.
323 #[arg(long)]
324 pub no_excluded: bool,
325
326 /// Make matched entries stat-dirty so that Git can re-index files
327 /// you wish to convert to LFS.
328 ///
329 /// Does not modify any `.gitattributes` file.
330 #[arg(long)]
331 pub no_modify_attrs: bool,
332}
333
334/// Remove Git LFS paths from Git attributes
335///
336/// Stop tracking the given path(s) through Git LFS. The argument can
337/// be a glob pattern or a file path. The matching pointer files in
338/// history (and the objects in the local store) are left in place.
339#[derive(Args)]
340pub struct UntrackArgs {
341 /// Paths or glob patterns to stop tracking.
342 pub patterns: Vec<String>,
343}
344
345/// Git filter process that converts between pointer and actual content
346///
347/// Implement the Git process filter API, exchanging handshake messages
348/// and then accepting and responding to requests to either clean or
349/// smudge a file.
350///
351/// `filter-process` is always run by Git's filter process, and is
352/// configured by the repository's Git attributes.
353///
354/// In your Git configuration or in a `.lfsconfig` file, you may set
355/// either or both of `lfs.fetchinclude` and `lfs.fetchexclude` to
356/// comma-separated lists of paths. If `lfs.fetchinclude` is defined,
357/// Git LFS pointer files will only be replaced with the contents of
358/// the corresponding object file if their path matches one in that
359/// list, and if `lfs.fetchexclude` is defined, pointer files will
360/// only be replaced if their path does not match one in that list.
361/// Paths are matched using wildcard matching as per gitignore(5).
362/// Pointer files that are not replaced are simply copied to standard
363/// output without change.
364///
365/// The filter process uses Git's pkt-line protocol to communicate, and
366/// is documented in detail in gitattributes(5).
367#[derive(Args)]
368pub struct FilterProcessArgs {
369 /// Skip automatic downloading of objects on clone or pull.
370 ///
371 /// Equivalent to `GIT_LFS_SKIP_SMUDGE=1`. Wired up by
372 /// `git lfs install --skip-smudge`.
373 #[arg(short, long)]
374 pub skip: bool,
375}
376
377/// Download all Git LFS files for a given ref
378///
379/// Download Git LFS objects at the given refs from the specified remote.
380/// See DEFAULT REMOTE and DEFAULT REFS for what happens if you don't
381/// specify.
382///
383/// This does not update the working copy; use git-lfs-pull(1) to
384/// download and replace pointer text with object content, or
385/// git-lfs-checkout(1) to materialize already-downloaded objects.
386#[derive(Args)]
387pub struct FetchArgs {
388 /// Optional remote name followed by refs. The first positional
389 /// argument is treated as a remote name when it resolves; any
390 /// following arguments are refs to fetch.
391 pub args: Vec<String>,
392
393 /// Specify `lfs.fetchinclude` just for this invocation; see
394 /// INCLUDE AND EXCLUDE.
395 #[arg(short = 'I', long, help_heading = FILTER)]
396 pub include: Vec<String>,
397
398 /// Specify `lfs.fetchexclude` just for this invocation; see
399 /// INCLUDE AND EXCLUDE.
400 #[arg(short = 'X', long, help_heading = FILTER)]
401 pub exclude: Vec<String>,
402
403 /// Download all objects that are referenced by any commit
404 /// reachable from the refs provided as arguments.
405 ///
406 /// If no refs are provided, then all refs are fetched. This is
407 /// primarily for backup and migration purposes. Cannot be
408 /// combined with `--include`/`--exclude`. Ignores any globally
409 /// configured include and exclude paths to ensure that all
410 /// objects are downloaded.
411 #[arg(short, long)]
412 pub all: bool,
413
414 /// Read a list of newline-delimited refs from standard input
415 /// instead of the command line.
416 #[arg(long)]
417 pub stdin: bool,
418
419 /// Prune old and unreferenced objects after fetching, equivalent
420 /// to running `git lfs prune` afterwards. See git-lfs-prune(1)
421 /// for more details.
422 #[arg(short, long)]
423 pub prune: bool,
424
425 /// Also fetch objects that are already present locally.
426 ///
427 /// Useful for recovery from a corrupt local store.
428 #[arg(long)]
429 pub refetch: bool,
430
431 /// Print what would be fetched, without actually fetching anything.
432 #[arg(short, long)]
433 pub dry_run: bool,
434
435 /// Also fetch recently-touched refs and the recent pre-images on
436 /// each.
437 ///
438 /// Walk every ref under `refs/heads/` (and, by default, every
439 /// remote-tracking ref) whose tip commit lies within
440 /// `lfs.fetchrecentrefsdays` of today, and on each of those refs
441 /// download the pre-image of every LFS file modified within
442 /// `lfs.fetchrecentcommitsdays`. Combine with the named refs'
443 /// HEAD-state fetch. The same behaviour fires automatically if
444 /// `lfs.fetchrecentalways` is set.
445 #[arg(short, long)]
446 pub recent: bool,
447
448 /// Write the details of all object transfer requests as JSON to
449 /// standard output.
450 ///
451 /// Intended for interoperation with external tools. When
452 /// `--dry-run` is also specified, writes the details of the
453 /// transfers that would occur if the objects were fetched.
454 #[arg(short, long)]
455 pub json: bool,
456}
457
458const FILTER: &str = "Filter options";
459
460/// Download all Git LFS files for current ref and checkout
461///
462/// Download Git LFS objects for the currently checked out ref, and
463/// update the working copy with the downloaded content if required.
464///
465/// This is generally equivalent to running `git lfs fetch [options]
466/// [<remote>]` followed by `git lfs checkout`. See git-lfs-checkout(1)
467/// for partial-clone, sparse-checkout, and bare-repository behavior
468/// (governed by the installed Git version and `GIT_ATTR_SOURCE`).
469///
470/// Requires `git lfs install` to have wired up the smudge filter. If
471/// the filter is missing, the fetch step still runs but the
472/// working-tree update is skipped with a hint to install.
473#[derive(Args)]
474pub struct PullArgs {
475 /// Optional remote name followed by refs.
476 ///
477 /// The first positional argument is treated as a remote name when
478 /// it resolves; any following arguments are refs to fetch. With
479 /// no arguments, the default remote is used.
480 pub args: Vec<String>,
481
482 /// Specify `lfs.fetchinclude` just for this invocation.
483 #[arg(short = 'I', long, help_heading = FILTER)]
484 pub include: Vec<String>,
485
486 /// Specify `lfs.fetchexclude` just for this invocation.
487 #[arg(short = 'X', long, help_heading = FILTER)]
488 pub exclude: Vec<String>,
489}
490
491/// Push queued large files to the Git LFS endpoint
492///
493/// Upload Git LFS files to the configured endpoint for the current Git
494/// remote. By default, filters out objects that are already referenced
495/// by the local clone of the remote (approximated via
496/// `refs/remotes/<remote>/*`); the server's batch API dedupes again,
497/// so a missing local tracking ref doesn't waste bandwidth.
498#[derive(Args)]
499pub struct PushArgs {
500 /// Remote to push to (e.g. `origin`). The remote's tracking refs
501 /// are excluded from the upload set so already-pushed objects
502 /// aren't sent again.
503 pub remote: String,
504
505 /// Refs (or, with `--object-id`, raw OIDs) to push. With `--all`,
506 /// restricts the all-refs walk to these; with `--stdin`, ignored
507 /// (a warning is emitted).
508 pub args: Vec<String>,
509
510 /// Print the files that would be pushed, without actually pushing
511 /// them.
512 #[arg(short, long)]
513 pub dry_run: bool,
514
515 /// Push all objects reachable from the refs given as arguments.
516 ///
517 /// If no refs are provided, all local refs are pushed. Note this
518 /// behavior differs from `git lfs fetch --all`, which fetches
519 /// every ref including refs outside `refs/heads` / `refs/tags`. If
520 /// you're migrating a repository, run `git lfs push` for any
521 /// additional remote refs that contain LFS objects not reachable
522 /// from your local refs.
523 #[arg(short, long)]
524 pub all: bool,
525
526 /// Push only the object OIDs listed on the command line (or read
527 /// from stdin with `--stdin`), separated by spaces.
528 #[arg(short, long)]
529 pub object_id: bool,
530
531 /// Read newline-delimited refs (or object IDs when using
532 /// `--object-id`) from standard input instead of the command
533 /// line.
534 #[arg(long)]
535 pub stdin: bool,
536}
537
538/// Efficiently clone a LFS-enabled repository
539///
540/// Clone an LFS-enabled Git repository by disabling LFS during the
541/// `git clone`, then running `git lfs pull` directly afterwards.
542/// Also installs the repo-level hooks (`.git/hooks`) that LFS requires
543/// to operate; if `--separate-git-dir` is given to `git clone`, the
544/// hooks are installed there.
545///
546/// Historically faster than a regular `git clone` because that would
547/// download LFS content via the smudge filter one file at a time.
548/// Modern `git clone` parallelizes the smudge filter, so this command
549/// no longer offers a meaningful speedup over plain `git clone`. You
550/// should prefer plain `git clone`.
551///
552/// In addition to the options accepted by `git clone`, the LFS-only
553/// flags `--include` / `-I <paths>`, `--exclude` / `-X <paths>`, and
554/// `--skip-repo` (skip installing the repo-level hooks) are accepted
555/// — see git-lfs-fetch(1) for the include/exclude semantics. They're
556/// parsed from the trailing argument list rather than declared as
557/// clap flags, so they don't appear in this command's `--help`.
558#[derive(Args)]
559pub struct CloneArgs {
560 /// `git clone` arguments plus the LFS pass-through flags
561 /// (`-I`/`--include`, `-X`/`--exclude`, `--skip-repo`). The
562 /// repository URL is required; an optional target directory
563 /// follows.
564 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
565 pub args: Vec<String>,
566}
567
568/// Git post-checkout hook implementation
569///
570/// Respond to Git post-checkout events. Git invokes this hook with
571/// `<rev-before> <ref-after> <is-branch-checkout>`. We make sure that
572/// any files which are marked as lockable by `git lfs track` are
573/// read-only in the working copy, if not currently locked by the
574/// local user.
575#[derive(Args)]
576pub struct PostCheckoutArgs {
577 /// Positional arguments passed by git. Not normally invoked by
578 /// hand.
579 pub args: Vec<String>,
580}
581
582/// Git post-commit hook implementation
583///
584/// Respond to Git post-commit events. Like `git lfs post-merge`, we
585/// make sure that any files which are marked as lockable by
586/// `git lfs track` are read-only in the working copy, if not
587/// currently locked by the local user.
588///
589/// Upstream optimizes by only checking files changed in HEAD; we
590/// currently scan the full work tree on every commit. The result is
591/// the same, but slower on large repositories.
592#[derive(Args)]
593pub struct PostCommitArgs {
594 /// Positional arguments passed by git. Not normally invoked by
595 /// hand.
596 pub args: Vec<String>,
597}
598
599/// Git post-merge hook implementation
600///
601/// Respond to Git post-merge events. Git invokes this hook with
602/// `<is-squash>`. We make sure that any files which are marked as
603/// lockable by `git lfs track` are read-only in the working copy, if
604/// not currently locked by the local user.
605#[derive(Args)]
606pub struct PostMergeArgs {
607 /// Positional arguments passed by git. Not normally invoked by
608 /// hand.
609 pub args: Vec<String>,
610}
611
612/// Git pre-push hook implementation
613///
614/// Respond to Git pre-push events. Reads the range of commits from
615/// stdin in the form `<local-ref> <local-sha1> <remote-ref>
616/// <remote-sha1>`, takes the remote name and URL as arguments, and
617/// uploads any Git LFS objects associated with those commits to the
618/// Git LFS API.
619///
620/// When pushing a new branch, the list of Git objects considered is
621/// every object reachable from the new branch. When deleting a
622/// branch, no LFS objects are pushed.
623#[derive(Args)]
624pub struct PrePushArgs {
625 /// Name of the remote being pushed to.
626 pub remote: String,
627
628 /// URL of the remote (informational; we use the `lfs.url`
629 /// config).
630 pub url: Option<String>,
631
632 /// Print the files that would be pushed, without actually
633 /// pushing them.
634 #[arg(short, long)]
635 pub dry_run: bool,
636}
637
638/// Print the git-lfs version banner and exit
639#[derive(Args)]
640pub struct VersionArgs;
641
642/// Build, compare, and check pointers
643///
644/// Build and optionally compare generated pointer files to ensure
645/// consistency between different Git LFS implementations.
646#[derive(Args)]
647pub struct PointerArgs {
648 // TODO(post-1.0): replace the --strict/--no-strict, --check/--pointer,
649 // and --check/--file/--stdin manual checks (cli/src/pointer_cmd.rs:108,
650 // 218, 223, 230, 241) with clap arg_group/conflicts_with/requires.
651 // No shell test asserts this wording, so the constraint here is
652 // softer than for the other commands — the deferral is purely about
653 // upstream parity. Worth taking whenever.
654 /// A local file to build the pointer from.
655 #[arg(short, long)]
656 pub file: Option<PathBuf>,
657
658 /// A local file containing a pointer generated from another
659 /// implementation.
660 ///
661 /// Compared to the pointer generated from `--file`.
662 #[arg(short, long)]
663 pub pointer: Option<PathBuf>,
664
665 /// Read the pointer from standard input to compare with the
666 /// pointer generated from `--file`.
667 #[arg(long)]
668 pub stdin: bool,
669
670 /// Read the pointer from standard input (with `--stdin`) or the
671 /// filepath (with `--file`).
672 ///
673 /// If neither or both of `--stdin` and `--file` are given, the
674 /// invocation is invalid. Exits 0 if the data read is a valid Git
675 /// LFS pointer, 1 otherwise. With `--strict`, exits 2 if the
676 /// pointer is not byte-canonical.
677 #[arg(long)]
678 pub check: bool,
679
680 /// With `--check`, verify that the pointer is canonical (the one
681 /// Git LFS would create).
682 ///
683 /// If it isn't, exits 2. The default — for backwards compatibility
684 /// — is `--no-strict`.
685 #[arg(long)]
686 pub strict: bool,
687
688 /// Disable strict mode (paired with `--strict`).
689 #[arg(long)]
690 pub no_strict: bool,
691
692 /// Build a plain pointer without running configured `lfs.extension.*`
693 /// clean commands. Default behavior is to chain through any
694 /// extensions (and emit a `warning:` line on stderr); pass this to
695 /// suppress both the chain and the warning.
696 #[arg(long)]
697 pub no_extensions: bool,
698}
699
700/// Display the Git LFS environment
701///
702/// Display the current Git LFS environment: version, endpoints,
703/// on-disk paths, and the three `filter.lfs.*` config values.
704#[derive(Args)]
705pub struct EnvArgs;
706
707/// List the configured LFS pointer extensions
708///
709/// Print each `lfs.extension.<name>.*` entry resolved to its final
710/// configuration in priority order. Extensions chain external
711/// clean / smudge programs around each LFS object — see
712/// git-lfs-config(5) for how to configure them.
713///
714/// With no arguments, prints every configured extension. With
715/// `list <name>...`, prints only the named extensions (one block
716/// per name, in argument order).
717#[derive(Args)]
718pub struct ExtArgs {
719 #[command(subcommand)]
720 pub cmd: Option<ExtCmd>,
721}
722
723#[derive(Subcommand)]
724pub enum ExtCmd {
725 List(ExtListArgs),
726}
727
728/// List configured LFS pointer extensions, optionally filtered by name.
729#[derive(Args)]
730pub struct ExtListArgs {
731 /// Extension names to print. With no names, prints all configured
732 /// extensions (same as bare `git lfs ext`).
733 pub names: Vec<String>,
734}
735
736/// Update Git hooks
737///
738/// Update the Git hooks used by Git LFS. Silently upgrades known hook
739/// contents. If you have your own custom hooks you may need to use
740/// one of the extended options below.
741#[derive(Args)]
742pub struct UpdateArgs {
743 /// Forcibly overwrite any existing hooks with git-lfs hooks.
744 ///
745 /// Use this option if `git lfs update` fails because of existing
746 /// hooks but you don't care about their current contents.
747 #[arg(short, long)]
748 pub force: bool,
749
750 /// Print instructions for manually updating your hooks to
751 /// include git-lfs functionality.
752 ///
753 /// Use this option if `git lfs update` fails because of existing
754 /// hooks and you want to retain their functionality.
755 #[arg(short, long)]
756 pub manual: bool,
757}
758
759/// Migrate history to or from Git LFS
760///
761/// Convert files in a Git repository to or from Git LFS pointers, or
762/// summarize Git file sizes by file type. The `import` mode converts
763/// Git files (i.e. blobs) to Git LFS, the `export` mode does the
764/// reverse, and the `info` mode provides an informational summary
765/// useful for deciding which files to import or export.
766///
767/// In all modes, by default `git lfs migrate` operates only on the
768/// currently checked-out branch, and only on files added in commits
769/// which do not exist on any remote. Multiple options are available
770/// to override these defaults — see INCLUDE AND EXCLUDE REFERENCES.
771///
772/// When converting files to or from Git LFS, this command only
773/// changes your local repository and working copy, never any remotes.
774/// `import` and `export` are generally DESTRUCTIVE — they rewrite Git
775/// history, changing commits and generating new commit SHAs. (The
776/// exception is the `--no-rewrite` `import` sub-mode.) Always commit
777/// or stash any uncommitted work first, validate the result before
778/// pushing, and force-push the new history once you're satisfied.
779///
780/// For `info` and `import`, all file types are considered by default.
781/// In `import` you'll usually want filename patterns or `--fixup`;
782/// `export` requires at least one `--include` pattern. See INCLUDE
783/// AND EXCLUDE.
784///
785/// `git lfs migrate` will examine, create, and modify `.gitattributes`
786/// files as necessary. They are always assigned the default
787/// read/write permissions mode; symbolic links with that name halt
788/// the migration.
789#[derive(Args)]
790pub struct MigrateArgs {
791 #[command(subcommand)]
792 pub cmd: MigrateCmd,
793}
794
795#[derive(Subcommand)]
796pub enum MigrateCmd {
797 Import(MigrateImportArgs),
798 Export(MigrateExportArgs),
799 Info(MigrateInfoArgs),
800}
801
802/// Convert Git objects to Git LFS pointers
803///
804/// Migrate objects present in the Git history to pointer files
805/// tracked and stored with Git LFS. Adds entries for the converted
806/// file types to `.gitattributes`, creating those files if they
807/// don't exist — as if `git lfs track` had been run at the points
808/// in history where each type first appears.
809///
810/// With `--fixup`, examine existing `.gitattributes` files and
811/// convert only Git objects that should be tracked by Git LFS
812/// according to those rules but aren't yet.
813///
814/// With `--no-rewrite`, migrate objects to pointers in a single new
815/// commit on top of HEAD without rewriting history. The base
816/// `migrate` options (`--include-ref`, `--everything`, etc.) are
817/// ignored in this sub-mode, and the positional argument list
818/// changes from branches to a list of files. Files must be tracked
819/// by patterns already in `.gitattributes`.
820#[derive(Args)]
821pub struct MigrateImportArgs {
822 // TODO(post-1.0): replace the manual --no-rewrite/--fixup/--above/
823 // --include/--exclude/--everything cross-flag validation
824 // (cli/src/migrate/import.rs:53-77, plus the shared
825 // --everything/positional check in migrate/mod.rs::resolve_refs)
826 // with clap arg_group/conflicts_with. Currently kept as-is because
827 // tests/t-migrate-fixup.sh:94,112,130 and t-migrate-import.sh:814,
828 // 825,836 assert upstream's exact wording (e.g. "--no-rewrite and
829 // --fixup cannot be combined", "Cannot use --everything with
830 // --include-ref or --exclude-ref"). Worth taking once we're free
831 // to update those assertions.
832 /// Branches to rewrite (default: the currently checked-out
833 /// branch). With `--no-rewrite`, instead a list of working-tree
834 /// files to convert. References prefixed with `^` are excluded.
835 pub args: Vec<String>,
836
837 /// Convert paths matching this glob (repeatable, comma-delimited).
838 /// Required unless `--above` is set or `--no-rewrite` is given.
839 #[arg(short = 'I', long = "include")]
840 pub include: Vec<String>,
841
842 /// Exclude paths matching this glob (repeatable, comma-delimited).
843 #[arg(short = 'X', long = "exclude")]
844 pub exclude: Vec<String>,
845
846 /// Restrict the rewrite to commits reachable from these refs.
847 /// Repeatable.
848 #[arg(long = "include-ref")]
849 pub include_ref: Vec<String>,
850
851 /// Exclude commits reachable from these refs. Repeatable.
852 #[arg(long = "exclude-ref")]
853 pub exclude_ref: Vec<String>,
854
855 /// Consider all commits reachable from any local or remote ref.
856 ///
857 /// Only local refs are updated even with `--everything`; remote
858 /// refs stay synchronized with their remote.
859 #[arg(long)]
860 pub everything: bool,
861
862 /// Only migrate files whose individual filesize is above the
863 /// given size (e.g. `1b`, `20 MB`, `3 TiB`).
864 ///
865 /// Cannot be used with `--include`, `--exclude`, or `--fixup`.
866 #[arg(long, default_value = "")]
867 pub above: String,
868
869 /// Migrate objects in a new commit on top of HEAD without
870 /// rewriting Git history.
871 ///
872 /// Switches to a different argument list (positional args become
873 /// files, not branches) and ignores the core `migrate` options
874 /// (`--include-ref`, `--everything`, etc.).
875 #[arg(long)]
876 pub no_rewrite: bool,
877
878 /// Commit message for the `--no-rewrite` commit.
879 ///
880 /// If omitted, a message is generated from the file arguments.
881 #[arg(short, long)]
882 pub message: Option<String>,
883
884 /// Infer `--include` and `--exclude` filters per-commit from the
885 /// repository's `.gitattributes` files.
886 ///
887 /// Imports filepaths that should be tracked by Git LFS but
888 /// aren't yet pointers. Incompatible with explicitly given
889 /// `--include` / `--exclude` filters.
890 #[arg(long)]
891 pub fixup: bool,
892
893 /// Write a CSV of `<OLD-SHA>,<NEW-SHA>` for every rewritten
894 /// commit to the named file.
895 #[arg(long = "object-map")]
896 pub object_map: Option<PathBuf>,
897
898 /// Print the commit OID and filename of migrated files to
899 /// standard output.
900 #[arg(long)]
901 pub verbose: bool,
902
903 /// Remote to consult when fetching missing LFS objects (default
904 /// `origin`).
905 #[arg(long)]
906 pub remote: Option<String>,
907
908 /// Don't refresh the known set of remote references before
909 /// determining the set of "un-pushed" commits to migrate.
910 ///
911 /// Has no effect when combined with `--include-ref` or
912 /// `--exclude-ref`.
913 #[arg(long)]
914 pub skip_fetch: bool,
915
916 /// Assume a yes answer to any prompts, permitting noninteractive
917 /// use.
918 ///
919 /// Currently we don't prompt for any reason, so this is accepted
920 /// as a no-op for upstream parity.
921 #[arg(long)]
922 pub yes: bool,
923}
924
925/// Convert Git LFS pointers to Git objects
926///
927/// Migrate Git LFS pointer files present in the Git history out of
928/// Git LFS, converting them back into their corresponding object
929/// files. Files matching the `--include` patterns are removed from
930/// Git LFS; files matching `--exclude` retain their LFS status.
931/// Modifies `.gitattributes` to set/unset the relevant filepath
932/// patterns.
933///
934/// At least one `--include` pattern is required. Objects not present
935/// in the local LFS store are downloaded from the `--remote`
936/// (defaults to `origin`). Pointers whose objects can't be fetched
937/// are left as-is.
938#[derive(Args)]
939pub struct MigrateExportArgs {
940 // TODO(post-1.0): make --include a required clap arg (it is required
941 // in practice — cli/src/migrate/export.rs:53). Currently kept as a
942 // runtime check because tests/t-migrate-export.sh:208 asserts
943 // upstream's exact wording ("One or more files must be specified
944 // with --include"); clap's "the following required arguments were
945 // not provided: --include <INCLUDE>" would be a strict UX win but
946 // a behavioral diff. Also see the shared --everything/positional
947 // check in migrate/mod.rs::resolve_refs.
948 /// Branches to rewrite (default: the currently checked-out
949 /// branch). References prefixed with `^` are excluded.
950 pub branches: Vec<String>,
951
952 /// Convert pointers at paths matching this glob (repeatable,
953 /// comma-delimited). Required — at least one must be given.
954 #[arg(short = 'I', long = "include")]
955 pub include: Vec<String>,
956
957 /// Don't convert pointers at paths matching this glob
958 /// (repeatable, comma-delimited).
959 #[arg(short = 'X', long = "exclude")]
960 pub exclude: Vec<String>,
961
962 /// Restrict the rewrite to commits reachable from these refs.
963 /// Repeatable.
964 #[arg(long = "include-ref")]
965 pub include_ref: Vec<String>,
966
967 /// Exclude commits reachable from these refs. Repeatable.
968 #[arg(long = "exclude-ref")]
969 pub exclude_ref: Vec<String>,
970
971 /// Consider all commits reachable from any local or remote ref.
972 ///
973 /// Only local refs are updated even with `--everything`; remote
974 /// refs stay synchronized with their remote.
975 #[arg(long)]
976 pub everything: bool,
977
978 /// Write a CSV of `<OLD-SHA>,<NEW-SHA>` for every rewritten
979 /// commit to the named file.
980 ///
981 /// Useful as input to `git filter-repo` or other downstream
982 /// tools.
983 #[arg(long = "object-map")]
984 pub object_map: Option<PathBuf>,
985
986 /// Print the commit OID and filename of migrated files to
987 /// standard output.
988 #[arg(long)]
989 pub verbose: bool,
990
991 /// Download LFS objects from this remote during the export.
992 /// Defaults to `origin`.
993 #[arg(long)]
994 pub remote: Option<String>,
995
996 /// Don't refresh the known set of remote references before the
997 /// rewrite.
998 #[arg(long)]
999 pub skip_fetch: bool,
1000
1001 /// Assume a yes answer to any prompts, permitting noninteractive
1002 /// use.
1003 ///
1004 /// Currently we don't prompt for any reason, so this is accepted
1005 /// as a no-op for upstream parity.
1006 #[arg(long)]
1007 pub yes: bool,
1008}
1009
1010/// Show information about repository size
1011///
1012/// Summarize the sizes of file objects present in the Git history,
1013/// grouped by filename extension. Read-only — no objects or history
1014/// change.
1015///
1016/// Existing Git LFS pointers are followed by default (the size of
1017/// the referenced objects is totaled in a separate "LFS Objects"
1018/// line). Use `--pointers=ignore` to skip pointers entirely, or
1019/// `--pointers=no-follow` to count the pointer-text size as if the
1020/// pointers were regular files (the older Git LFS behavior).
1021#[derive(Args)]
1022pub struct MigrateInfoArgs {
1023 // TODO(post-1.0): replace the manual --everything/--include-ref/
1024 // --exclude-ref/--fixup/--pointers/--include/--exclude cross-flag
1025 // validation (cli/src/migrate/info.rs:59-83, plus the shared
1026 // --everything/positional check in migrate/mod.rs::resolve_refs)
1027 // with clap arg_group/conflicts_with. Currently kept as-is because
1028 // tests/t-migrate-info.sh:903,922,941,977,995,1013,1031 assert
1029 // upstream's exact wording (e.g. "Cannot use --fixup with
1030 // --pointers=follow"). The value-conditional --pointers checks
1031 // ("=follow" / "=no-follow") may not all collapse cleanly to
1032 // declarative clap rules.
1033 /// Branches to scan (default: the currently checked-out branch).
1034 /// References prefixed with `^` are excluded.
1035 pub branches: Vec<String>,
1036
1037 /// Only include paths matching this glob (repeatable,
1038 /// comma-delimited).
1039 #[arg(short = 'I', long = "include")]
1040 pub include: Vec<String>,
1041
1042 /// Exclude paths matching this glob (repeatable, comma-delimited).
1043 #[arg(short = 'X', long = "exclude")]
1044 pub exclude: Vec<String>,
1045
1046 /// Restrict the scan to commits reachable from these refs.
1047 /// Repeatable.
1048 #[arg(long = "include-ref")]
1049 pub include_ref: Vec<String>,
1050
1051 /// Exclude commits reachable from these refs. Repeatable.
1052 #[arg(long = "exclude-ref")]
1053 pub exclude_ref: Vec<String>,
1054
1055 /// Consider all commits reachable from any local or remote ref.
1056 #[arg(long)]
1057 pub everything: bool,
1058
1059 /// Only count files whose individual filesize is above the given
1060 /// size (e.g. `1b`, `20 MB`, `3 TiB`).
1061 ///
1062 /// File-extension groups whose largest file is below `--above`
1063 /// don't appear in the output.
1064 #[arg(long, default_value = "")]
1065 pub above: String,
1066
1067 /// Display the top N entries, ordered by total file count.
1068 ///
1069 /// Default 5. When existing Git LFS objects are found, an extra
1070 /// "LFS Objects" line is output in addition to the top N
1071 /// entries (unless `--pointers` changes this).
1072 #[arg(long, default_value_t = 5)]
1073 pub top: usize,
1074
1075 /// How to handle existing LFS pointer blobs.
1076 ///
1077 /// `follow` (default): summarize referenced objects in a separate
1078 /// "LFS Objects" line. `ignore`: skip pointers entirely.
1079 /// `no-follow`: count pointer-text size as if pointers were
1080 /// regular files (the older Git LFS behavior). When `--fixup` is
1081 /// given, defaults to `ignore`.
1082 #[arg(long)]
1083 pub pointers: Option<String>,
1084
1085 /// Format byte quantities in this unit.
1086 ///
1087 /// Valid units: `b, kib, mib, gib, tib, pib` (IEC) or
1088 /// `b, kb, mb, gb, tb, pb` (SI). Auto-scaled when omitted.
1089 #[arg(long)]
1090 pub unit: Option<String>,
1091
1092 /// Infer `--include` and `--exclude` filters per-commit from the
1093 /// repository's `.gitattributes` files.
1094 ///
1095 /// Counts filepaths that should be tracked by Git LFS but aren't
1096 /// yet pointers. Incompatible with explicit `--include` /
1097 /// `--exclude` filters and with `--pointers` settings other than
1098 /// `ignore`. Implies `--pointers=ignore` if not set.
1099 #[arg(long)]
1100 pub fixup: bool,
1101
1102 /// Don't refresh the known set of remote references before the
1103 /// scan.
1104 #[arg(long)]
1105 pub skip_fetch: bool,
1106
1107 /// Remote to consult (currently a no-op; reserved for the
1108 /// auto-fetch path).
1109 #[arg(long)]
1110 pub remote: Option<String>,
1111}
1112
1113/// Populate working copy with real content from Git LFS files.
1114///
1115/// Try to ensure that the working copy contains file content for Git LFS
1116/// objects for the current ref, if the object data is available. Does not
1117/// download any content; see git-lfs-fetch(1) for that.
1118///
1119/// Checkout scans the current ref for all LFS objects that would be
1120/// required, then where a file is either missing in the working copy, or
1121/// contains placeholder pointer content with the same SHA, the real file
1122/// content is written, provided we have it in the local store. Modified
1123/// files are never overwritten.
1124///
1125/// One or more may be provided as arguments to restrict the set of files
1126/// that are updated. Glob patterns are matched as per the format described
1127/// in gitignore(5).
1128///
1129/// When used with `--to` and the working tree is in a conflicted state due
1130/// to a merge, this option checks out one of the three stages a conflicting
1131/// Git LFS object into a separate file (which can be outside of the work
1132/// tree). This can make using diff tools to inspect and resolve merges
1133/// easier. A single Git LFS object's file path must be provided in
1134/// `PATHS`. If `FILE` already exists, whether as a regular
1135/// file, symbolic link, or directory, it will be removed and replaced, unless
1136/// it is a non-empty directory or otherwise cannot be deleted.
1137///
1138/// If the installed Git version is at least 2.42.0,
1139/// this command will by default check out Git LFS objects for files
1140/// only if they are present in the Git index and if they match a Git LFS
1141/// filter attribute from a `.gitattributes` file that is present in either
1142/// the index or the current working tree (or, as is always the case, if
1143/// they match a Git LFS filter attribute in a local gitattributes file
1144/// such as `$GIT_DIR/info/attributes`). These constraints do not apply
1145/// with prior versions of Git.
1146///
1147/// In a repository with a partial clone or sparse checkout, it is therefore
1148/// advisable to check out all `.gitattributes` files from HEAD before
1149/// using this command, if Git v2.42.0 or later is installed. Alternatively,
1150/// the `GIT_ATTR_SOURCE` environment variable may be set to HEAD, which
1151/// will cause Git to only read attributes from `.gitattributes` files in
1152/// HEAD and ignore those in the index or working tree.
1153///
1154/// In a bare repository, this command prints an informational message and
1155/// exits without modifying anything. In a future version, it may exit with
1156/// an error.
1157#[derive(Args)]
1158pub struct CheckoutArgs {
1159 // TODO(post-1.0): replace this manual stage/--to validation with
1160 // clap arg_group/requires/conflicts_with. Currently kept as-is
1161 // because tests/t-checkout.sh:897-909 assert upstream's exact error
1162 // wording; clap's wording would be a strict UX improvement but a
1163 // behavioral diff. Worth taking once we're free to update those
1164 // assertions.
1165 /// Check out the merge base of the specified file
1166 #[arg(long)]
1167 pub base: bool,
1168
1169 /// Check out our side (that of the current branch) of the
1170 /// conflict for the specified file
1171 #[arg(long)]
1172 pub ours: bool,
1173
1174 /// Check out their side (that of the other branch) of the
1175 /// conflict for the specified file
1176 #[arg(long)]
1177 pub theirs: bool,
1178
1179 /// If the working tree is in a conflicted state, check out the
1180 /// portion of the conflict specified by `--base`, `--ours`, or
1181 /// `--theirs` to the given path. Exactly one of these options
1182 /// is required.
1183 #[arg(long, value_name = "FILE")]
1184 pub to: Option<String>,
1185
1186 /// Paths to check out.
1187 ///
1188 /// When empty, everything in HEAD's tree is checked out. In
1189 /// conflict mode (`--to <path>` together with one of `--base`,
1190 /// `--ours`, or `--theirs`), exactly one path is required.
1191 pub paths: Vec<String>,
1192}
1193
1194/// Delete old LFS files from local storage
1195///
1196/// Delete locally stored LFS objects that aren't reachable from HEAD
1197/// or any unpushed commit, freeing up disk space.
1198#[derive(Args)]
1199pub struct PruneArgs {
1200 /// Don't actually delete anything; just report what would have
1201 /// been done.
1202 #[arg(short, long)]
1203 pub dry_run: bool,
1204
1205 /// Report the full detail of what is/would be deleted.
1206 #[arg(short, long)]
1207 pub verbose: bool,
1208
1209 /// Ignore the recent-refs / recent-commits retention windows
1210 /// when computing what is prunable. Equivalent to setting
1211 /// `lfs.fetchrecentrefsdays` and `lfs.fetchrecentcommitsdays` to
1212 /// 0 for this invocation.
1213 #[arg(long)]
1214 pub recent: bool,
1215
1216 /// Treat every pushed object as prunable regardless of the
1217 /// recent-refs / recent-commits / unpushed retention rules.
1218 /// Pointers reachable from HEAD's tree are still kept.
1219 #[arg(short, long)]
1220 pub force: bool,
1221
1222 /// Verify with the remote that prunable objects exist there
1223 /// before deleting them locally. With this on, an object that
1224 /// can't be served by the remote either halts the prune (default)
1225 /// or is dropped from the delete set (`--when-unverified=continue`).
1226 /// Reachable-but-unverified objects are reported as `missing on
1227 /// remote:`; unreachable objects (orphans not in any commit) are
1228 /// silently passed through unless `--verify-unreachable` is also
1229 /// set. Overrides `lfs.pruneverifyremotealways`.
1230 #[arg(short = 'c', long, conflicts_with = "no_verify_remote")]
1231 pub verify_remote: bool,
1232
1233 /// Override `lfs.pruneverifyremotealways=true` and skip the
1234 /// remote verify pass for this invocation.
1235 #[arg(long)]
1236 pub no_verify_remote: bool,
1237
1238 /// When `--verify-remote` is in effect, verify orphan objects
1239 /// (not reachable from any commit) too. Without this, orphans
1240 /// pass through verification silently and are still pruned.
1241 /// Overrides `lfs.pruneverifyunreachablealways`.
1242 #[arg(long, conflicts_with = "no_verify_unreachable")]
1243 pub verify_unreachable: bool,
1244
1245 /// Override `lfs.pruneverifyunreachablealways=true` and skip
1246 /// orphan verification for this invocation.
1247 #[arg(long)]
1248 pub no_verify_unreachable: bool,
1249
1250 /// What to do when `--verify-remote` finds objects missing on
1251 /// the remote. `halt` (the default) refuses the prune and lists
1252 /// the missing OIDs; `continue` drops them from the delete set
1253 /// and prunes the verified ones.
1254 #[arg(long, value_name = "MODE", default_value = "halt", value_parser = ["halt", "continue"])]
1255 pub when_unverified: String,
1256}
1257
1258/// Check Git LFS files for consistency
1259///
1260/// Check all Git LFS files in the current HEAD for consistency.
1261/// Corrupted files are moved to `.git/lfs/bad`.
1262///
1263/// A single committish may be given to inspect that commit instead of
1264/// HEAD. The `<a>..<b>` range form from upstream is not yet supported
1265/// — only a single ref is accepted. With no argument, HEAD is
1266/// examined.
1267///
1268/// The default is to perform all checks. `lfs.fetchexclude` is also
1269/// not yet honored on this command; objects whose paths match the
1270/// exclude list will still be checked.
1271#[derive(Args)]
1272pub struct FsckArgs {
1273 /// Ref to scan. Defaults to HEAD.
1274 pub refspec: Option<String>,
1275
1276 /// Check that each object in HEAD matches its expected hash and
1277 /// that each object exists on disk.
1278 #[arg(long)]
1279 pub objects: bool,
1280
1281 /// Check that each pointer is canonical and that each file which
1282 /// should be stored as a Git LFS file is so stored.
1283 #[arg(long)]
1284 pub pointers: bool,
1285
1286 /// Perform checks, but do not move any corrupted files to
1287 /// `.git/lfs/bad`.
1288 #[arg(short, long)]
1289 pub dry_run: bool,
1290}
1291
1292/// Show the status of Git LFS files in the working tree
1293///
1294/// Display paths of Git LFS objects that have not been pushed to the
1295/// Git LFS server (large files that would be uploaded by `git push`),
1296/// that have differences between the index file and the current HEAD
1297/// commit (large files that would be committed by `git commit`), or
1298/// that have differences between the working tree and the index file
1299/// (files that could be staged with `git add`).
1300///
1301/// Must be run in a non-bare repository.
1302#[derive(Args)]
1303pub struct StatusArgs {
1304 /// Give the output in an easy-to-parse format for scripts.
1305 #[arg(short, long)]
1306 pub porcelain: bool,
1307
1308 /// Write Git LFS file status information as JSON to standard
1309 /// output if the command exits successfully.
1310 ///
1311 /// Intended for interoperation with external tools. If
1312 /// `--porcelain` is also provided, that option takes precedence.
1313 #[arg(short, long)]
1314 pub json: bool,
1315}
1316
1317/// Set a file as "locked" on the Git LFS server
1318///
1319/// Sets the given file path as "locked" against the Git LFS server,
1320/// with the intention of blocking attempts by other users to update
1321/// the given path. Locking a file requires the file to exist in the
1322/// working copy.
1323///
1324/// Once locked, LFS will verify that Git pushes do not modify files
1325/// locked by other users. See the description of the
1326/// `lfs.<url>.locksverify` config key in git-lfs-config(5) for
1327/// details.
1328#[derive(Args)]
1329pub struct LockArgs {
1330 /// Paths to lock. Repo-relative or absolute; must resolve inside
1331 /// the working tree. Upstream's CLI accepts a single path; ours
1332 /// accepts multiple (additive extension).
1333 pub paths: Vec<String>,
1334
1335 /// Specify the Git LFS server to use. Ignored if the `lfs.url`
1336 /// config key is set.
1337 #[arg(short, long)]
1338 pub remote: Option<String>,
1339
1340 /// Write lock info as JSON to standard output if the command
1341 /// exits successfully.
1342 ///
1343 /// Intended for interoperation with external tools. If the command
1344 /// returns with a non-zero exit code, plain text messages are sent
1345 /// to standard error.
1346 #[arg(short, long)]
1347 pub json: bool,
1348
1349 /// Refspec to associate the lock with (extension over upstream).
1350 ///
1351 /// Defaults to the current branch's tracked upstream
1352 /// (`branch.<current>.merge`) or the current branch's full ref
1353 /// (`refs/heads/<branch>`).
1354 #[arg(long = "ref")]
1355 pub refspec: Option<String>,
1356}
1357
1358/// Lists currently locked files from the Git LFS server
1359///
1360/// Lists current locks from the Git LFS server. Without filters, all
1361/// locks visible to the configured remote are returned.
1362#[derive(Args)]
1363pub struct LocksArgs {
1364 /// Specify the Git LFS server to use. Ignored if the `lfs.url`
1365 /// config key is set.
1366 #[arg(short, long)]
1367 pub remote: Option<String>,
1368
1369 /// Specify a lock by its ID. Returns a single result.
1370 #[arg(short, long)]
1371 pub id: Option<String>,
1372
1373 /// Specify a lock by its path. Returns a single result.
1374 #[arg(short, long)]
1375 pub path: Option<String>,
1376
1377 /// List only our own locks which are cached locally. Skips a
1378 /// remote call.
1379 ///
1380 /// Useful when offline or to confirm what `git lfs lock` recorded
1381 /// locally. Combine with `--path` / `--id` / `--limit` to filter;
1382 /// `--verify` is rejected.
1383 #[arg(long)]
1384 pub local: bool,
1385
1386 /// Verify the lock owner on the server and mark our own locks
1387 /// with `O`.
1388 ///
1389 /// Own locks are held by us and the corresponding files can be
1390 /// updated for the next push. All other locks are held by someone
1391 /// else. Contrary to `--local`, this also detects locks held by us
1392 /// despite no local lock information being available (e.g. because
1393 /// the file had been locked from a different clone) and detects
1394 /// "broken" locks (e.g. someone else forcibly unlocked our files).
1395 #[arg(long)]
1396 pub verify: bool,
1397
1398 /// Maximum number of results to return.
1399 #[arg(short, long)]
1400 pub limit: Option<u32>,
1401
1402 /// Write lock info as JSON to standard output if the command
1403 /// exits successfully.
1404 ///
1405 /// Intended for interoperation with external tools. If the command
1406 /// returns with a non-zero exit code, plain text messages are sent
1407 /// to standard error.
1408 #[arg(short, long)]
1409 pub json: bool,
1410
1411 /// Refspec to filter locks by (extension over upstream).
1412 ///
1413 /// Defaults to the current branch's tracked upstream — same
1414 /// auto-resolution as `git lfs lock`.
1415 #[arg(long = "ref")]
1416 pub refspec: Option<String>,
1417}
1418
1419/// Remove "locked" setting for a file on the Git LFS server
1420///
1421/// Removes the given file path as "locked" on the Git LFS server.
1422/// Files must exist and have a clean git status before they can be
1423/// unlocked. The `--force` flag will skip these checks.
1424#[derive(Args)]
1425pub struct UnlockArgs {
1426 // TODO(post-1.0): replace the manual --id-xor-paths check
1427 // (cli/src/lock.rs:301-306) with a clap ArgGroup
1428 // (required = true, multiple = false) covering --id and the
1429 // positional paths arg. Currently kept as-is because
1430 // tests/t-unlock.sh:228,431,482 assert upstream's exact wording
1431 // ("Exactly one of --id or a set of paths must be provided").
1432 // Worth taking once we're free to update those assertions.
1433 /// Paths to unlock. Upstream's CLI accepts a single path; ours
1434 /// accepts multiple (additive extension). Mutually exclusive
1435 /// with `--id`.
1436 pub paths: Vec<String>,
1437
1438 /// Specify the Git LFS server to use. Ignored if the `lfs.url`
1439 /// config key is set.
1440 #[arg(short, long)]
1441 pub remote: Option<String>,
1442
1443 /// Tell the server to remove the lock, even if it's owned by
1444 /// another user.
1445 #[arg(short, long)]
1446 pub force: bool,
1447
1448 /// Specify a lock by its ID instead of path. Mutually exclusive
1449 /// with the positional paths.
1450 #[arg(short, long)]
1451 pub id: Option<String>,
1452
1453 /// Write lock info as JSON to standard output if the command
1454 /// exits successfully.
1455 ///
1456 /// Intended for interoperation with external tools. If the command
1457 /// returns with a non-zero exit code, plain text messages are sent
1458 /// to standard error.
1459 #[arg(short, long)]
1460 pub json: bool,
1461
1462 /// Refspec to send with the unlock request (extension over
1463 /// upstream).
1464 ///
1465 /// Defaults to the current branch's tracked upstream — same
1466 /// auto-resolution as `git lfs lock`.
1467 #[arg(long = "ref")]
1468 pub refspec: Option<String>,
1469}
1470
1471/// Show information about Git LFS files in the index and working tree
1472///
1473/// Display paths of Git LFS files that are found in the tree at the
1474/// given reference. If no reference is given, scan the currently
1475/// checked-out branch.
1476///
1477/// An asterisk (`*`) after the OID indicates a full object, a minus
1478/// (`-`) indicates an LFS pointer.
1479///
1480/// Note: upstream's `--include` / `--exclude` path filters aren't
1481/// yet supported. The two-references form (`git lfs ls-files <a> <b>`,
1482/// to show files modified between two refs) is also not yet
1483/// supported.
1484#[derive(Args)]
1485pub struct LsFilesArgs {
1486 /// Ref to list. Defaults to HEAD.
1487 pub refspec: Option<String>,
1488
1489 /// Show the entire 64-character OID, instead of just the first
1490 /// 10.
1491 #[arg(short, long)]
1492 pub long: bool,
1493
1494 /// Show the size of the LFS object in parentheses at the end of
1495 /// each line.
1496 #[arg(short, long)]
1497 pub size: bool,
1498
1499 /// Show only the LFS-tracked file names.
1500 #[arg(short, long)]
1501 pub name_only: bool,
1502
1503 /// Inspect the full history of the repository, not the current
1504 /// HEAD (or other provided reference).
1505 ///
1506 /// Includes previous versions of LFS objects that are no longer
1507 /// found in the current tree.
1508 #[arg(short, long)]
1509 pub all: bool,
1510
1511 /// Show as much information as possible about an LFS file.
1512 ///
1513 /// Intended for manual inspection; the exact format may change
1514 /// at any time.
1515 #[arg(short, long)]
1516 pub debug: bool,
1517
1518 /// Include LFS pointers reachable from history but no longer
1519 /// present in the current tree.
1520 #[arg(long)]
1521 pub deleted: bool,
1522
1523 /// Write Git LFS file information as JSON to standard output if
1524 /// the command exits successfully.
1525 ///
1526 /// Intended for interoperation with external tools. If `--debug`
1527 /// is also provided, that option takes precedence. If any of
1528 /// `--long`, `--size`, or `--name-only` are provided, those
1529 /// options will have no effect.
1530 #[arg(short, long)]
1531 pub json: bool,
1532}
1533
1534/// Show errors logged by Git LFS
1535///
1536/// Manages the local log directory under `.git/lfs/logs`. Run with no
1537/// subcommand to list log filenames; `last` prints the most recent
1538/// log; `show <name>` prints a specific log; `clear` deletes them
1539/// all. `boomtown` is a self-test that intentionally panics, writes a
1540/// log file, and exits non-zero.
1541#[derive(Args)]
1542pub struct LogsArgs {
1543 #[command(subcommand)]
1544 pub sub: Option<LogsSub>,
1545}
1546
1547#[derive(Subcommand)]
1548pub enum LogsSub {
1549 /// Print the most recent log to stdout.
1550 Last,
1551 /// Print the named log to stdout.
1552 Show {
1553 /// Log filename (relative to `.git/lfs/logs`).
1554 name: String,
1555 },
1556 /// Delete every log under `.git/lfs/logs`.
1557 Clear,
1558 /// Self-test: write a sample crash log and exit with status 2.
1559 Boomtown,
1560}
1561
1562/// Merge driver for LFS-tracked files
1563///
1564/// Invoked by Git through a `merge.<name>.driver` configuration entry,
1565/// typically wired up as:
1566///
1567/// ```text
1568/// [merge "lfs"]
1569/// name = LFS merge driver
1570/// driver = git lfs merge-driver --ancestor %O --current %A --other %B --marker-size %L --output %A
1571/// ```
1572///
1573/// For each of `--ancestor`, `--current`, and `--other`, the input file
1574/// is either a pointer (smudged through to its working-tree content,
1575/// fetching the object on demand if necessary) or already plain content
1576/// (used as-is). The three resulting files plus a fresh tempfile for
1577/// the merged output are substituted into `--program` (default
1578/// `git merge-file --stdout --marker-size=%L %A %O %B >%D`) and run
1579/// via `sh -c`. The merged content is then cleaned back into a pointer
1580/// and written to `--output`. Non-zero exit from the merge program
1581/// indicates conflicts; that exit code is propagated.
1582#[derive(Args)]
1583pub struct MergeDriverArgs {
1584 /// File containing the ancestor (merge-base) version. Pointer or
1585 /// raw content; substituted for `%O` in the program template.
1586 #[arg(long)]
1587 pub ancestor: Option<String>,
1588
1589 /// File containing the current (`ours`) version. Pointer or raw
1590 /// content; substituted for `%A` in the program template.
1591 #[arg(long)]
1592 pub current: Option<String>,
1593
1594 /// File containing the other (`theirs`) version. Pointer or raw
1595 /// content; substituted for `%B` in the program template.
1596 #[arg(long)]
1597 pub other: Option<String>,
1598
1599 /// Path to write the merged pointer to. Typically the same path
1600 /// as `--current` so that Git picks up the result.
1601 #[arg(long)]
1602 pub output: Option<String>,
1603
1604 /// Merge program template. Defaults to
1605 /// `git merge-file --stdout --marker-size=%L %A %O %B >%D`. `%A`,
1606 /// `%O`, `%B`, `%D`, and `%L` are substituted with shell-quoted
1607 /// paths / the marker size; `%%` emits a literal `%`.
1608 #[arg(long)]
1609 pub program: Option<String>,
1610
1611 /// Conflict marker size to substitute for `%L`.
1612 #[arg(long, default_value_t = 7)]
1613 pub marker_size: u32,
1614}