Skip to main content

git_lfs/
args.rs

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