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
9use std::path::PathBuf;
10
11use clap::{Parser, Subcommand};
12
13#[derive(Parser)]
14#[command(
15 name = "git-lfs",
16 about = "Git LFS — large file storage for git",
17 // We want `git lfs --version` to print the same banner as
18 // `git lfs version`. clap's auto-derived `--version` would
19 // emit `git-lfs <version>` (one token, no `/` separator),
20 // which doesn't match the user-agent style upstream uses.
21 // Suppress clap's flag and handle --version ourselves.
22 disable_version_flag = true,
23 max_term_width = 100,
24)]
25pub struct Cli {
26 /// Print the version banner and exit.
27 #[arg(long, short = 'V', global = true)]
28 pub version: bool,
29
30 #[command(subcommand)]
31 pub command: Option<Command>,
32}
33
34#[derive(Subcommand)]
35pub enum MigrateCmd {
36 /// Rewrite history so files matching the include filter become LFS
37 /// pointers. With `--no-rewrite`, history is preserved and one
38 /// new commit is appended on top of HEAD with the named paths
39 /// converted in place.
40 Import {
41 /// Without `--no-rewrite`: branches/refs to rewrite (empty =
42 /// current branch). With `--no-rewrite`: working-tree paths
43 /// to convert.
44 args: Vec<String>,
45 /// Walk every local branch and tag.
46 #[arg(long)]
47 everything: bool,
48 /// Convert paths matching this glob (repeatable). Required
49 /// unless `--above` is set or `--no-rewrite` is given.
50 #[arg(short = 'I', long = "include")]
51 include: Vec<String>,
52 /// Exclude paths matching this glob (repeatable).
53 #[arg(short = 'X', long = "exclude")]
54 exclude: Vec<String>,
55 /// Restrict the rewrite to commits reachable from these refs.
56 /// Repeatable.
57 #[arg(long = "include-ref")]
58 include_ref: Vec<String>,
59 /// Exclude commits reachable from these refs. Repeatable.
60 #[arg(long = "exclude-ref")]
61 exclude_ref: Vec<String>,
62 /// Only convert files at least this large (e.g. `1mb`,
63 /// `500k`).
64 #[arg(long, default_value = "")]
65 above: String,
66 /// Don't rewrite history. Read named paths from the working
67 /// tree, convert in place, append one new commit on top of
68 /// HEAD.
69 #[arg(long)]
70 no_rewrite: bool,
71 /// Commit message for the `--no-rewrite` commit.
72 #[arg(short, long)]
73 message: Option<String>,
74 /// Skip the prompt confirming history rewrite. Currently we
75 /// never prompt, so this is accepted as a no-op for parity
76 /// with upstream's CLI surface.
77 #[arg(long)]
78 yes: bool,
79 /// Walk every commit and convert files that *should* be LFS
80 /// pointers (per their commit's `.gitattributes`) but
81 /// currently aren't. Mutually exclusive with `--include`,
82 /// `--exclude`, `--no-rewrite`.
83 #[arg(long)]
84 fixup: bool,
85 /// Don't fetch missing LFS objects from the remote before the
86 /// rewrite — accepted as a no-op since we never auto-fetch
87 /// today.
88 #[arg(long)]
89 skip_fetch: bool,
90 /// Write a comma-separated `<old>,<new>` mapping of every
91 /// rewritten commit OID to the named file.
92 #[arg(long = "object-map")]
93 object_map: Option<std::path::PathBuf>,
94 /// Print a per-commit progress line as the rewrite walks
95 /// history.
96 #[arg(long)]
97 verbose: bool,
98 /// Remote to consult when fetching missing LFS objects (default
99 /// `origin`).
100 #[arg(long)]
101 remote: Option<String>,
102 },
103 /// Inverse of import: rewrite history so LFS pointers become the
104 /// raw bytes they reference. Requires the LFS objects to already
105 /// be in the local store — `git lfs fetch` first if not. Pointers
106 /// whose objects are missing are left as-is.
107 Export {
108 /// Branches / refs to rewrite. Empty = current branch.
109 branches: Vec<String>,
110 /// Walk every local branch and tag.
111 #[arg(long)]
112 everything: bool,
113 /// Convert pointers at paths matching this glob (repeatable).
114 /// Required.
115 #[arg(short = 'I', long = "include")]
116 include: Vec<String>,
117 /// Don't convert pointers at paths matching this glob.
118 #[arg(short = 'X', long = "exclude")]
119 exclude: Vec<String>,
120 /// Restrict the rewrite to commits reachable from these refs.
121 /// Repeatable.
122 #[arg(long = "include-ref")]
123 include_ref: Vec<String>,
124 /// Exclude commits reachable from these refs. Repeatable.
125 #[arg(long = "exclude-ref")]
126 exclude_ref: Vec<String>,
127 /// Don't fetch missing LFS objects from the remote before the
128 /// rewrite — leave their pointers in place.
129 #[arg(long)]
130 skip_fetch: bool,
131 /// Write a comma-separated `<old>,<new>` mapping of every
132 /// rewritten commit OID to the named file. Useful as input to
133 /// `git filter-repo` or other downstream tools.
134 #[arg(long = "object-map")]
135 object_map: Option<std::path::PathBuf>,
136 /// Print a per-commit progress line as the rewrite walks
137 /// history.
138 #[arg(long)]
139 verbose: bool,
140 /// Remote to consult when fetching missing LFS objects (default
141 /// `origin`).
142 #[arg(long)]
143 remote: Option<String>,
144 /// Skip the prompt confirming history rewrite. Currently we
145 /// never prompt, so this is accepted as a no-op for parity
146 /// with upstream's CLI surface.
147 #[arg(long)]
148 yes: bool,
149 },
150 /// Walk history and report file extensions by total size.
151 /// Read-only — no objects or history change.
152 Info {
153 /// Branches / refs to scan. Empty = current branch.
154 branches: Vec<String>,
155 /// Walk every local branch and tag.
156 #[arg(long)]
157 everything: bool,
158 /// Only include paths matching this glob (repeatable).
159 #[arg(short = 'I', long = "include")]
160 include: Vec<String>,
161 /// Exclude paths matching this glob (repeatable).
162 #[arg(short = 'X', long = "exclude")]
163 exclude: Vec<String>,
164 /// Restrict the scan to commits reachable from these refs.
165 /// Repeatable.
166 #[arg(long = "include-ref")]
167 include_ref: Vec<String>,
168 /// Exclude commits reachable from these refs. Repeatable.
169 #[arg(long = "exclude-ref")]
170 exclude_ref: Vec<String>,
171 /// Only count files at least this large (e.g. `1mb`, `500k`).
172 #[arg(long, default_value = "")]
173 above: String,
174 /// Maximum extension rows to show.
175 #[arg(long, default_value_t = 5)]
176 top: usize,
177 /// How to handle existing LFS pointer blobs:
178 /// `follow` (default), `ignore`, or `no-follow`. Defaults
179 /// based on `--fixup`: `ignore` with the flag, `follow`
180 /// without.
181 #[arg(long)]
182 pointers: Option<String>,
183 /// Force the size unit for byte counts (`b`, `kb`, `mb`,
184 /// `gb`, `tb`, `pb`). Auto-scaled when omitted.
185 #[arg(long)]
186 unit: Option<String>,
187 /// Don't fetch missing LFS objects from the remote — accepted
188 /// as a no-op since we don't auto-fetch today.
189 #[arg(long)]
190 skip_fetch: bool,
191 /// Remote to consult (no-op for now; reserved for the
192 /// auto-fetch path).
193 #[arg(long)]
194 remote: Option<String>,
195 /// Walk history looking for files that *should* be LFS but
196 /// aren't (per `.gitattributes`). Implies `--pointers=ignore`.
197 #[arg(long)]
198 fixup: bool,
199 },
200}
201
202#[derive(Subcommand)]
203pub enum Command {
204 /// Run the clean filter: read content on stdin, write a pointer on stdout.
205 Clean {
206 /// Working-tree path of the file being cleaned. Substituted for
207 /// `%f` in any configured `lfs.extension.<name>.clean` command.
208 path: Option<PathBuf>,
209 },
210 /// Run the smudge filter: read a pointer on stdin, write content on stdout.
211 Smudge {
212 /// Working-tree path of the file being smudged (currently unused).
213 path: Option<PathBuf>,
214 /// Pass the pointer text through unchanged; equivalent to
215 /// `GIT_LFS_SKIP_SMUDGE=1`. Wired up by `install --skip-smudge`.
216 #[arg(long)]
217 skip: bool,
218 },
219 /// Configure git to invoke git-lfs as the clean/smudge/process filter,
220 /// and install the LFS git hooks.
221 Install {
222 /// Set config in the local repo only (default: --global).
223 #[arg(short, long)]
224 local: bool,
225 /// Operate on `/etc/gitconfig` (`git config --system`).
226 #[arg(long)]
227 system: bool,
228 /// Operate on `.git/config.worktree` for the current worktree.
229 #[arg(long)]
230 worktree: bool,
231 /// Operate on the given config file directly. Treated as
232 /// "global-like" for the success message.
233 #[arg(long, value_name = "PATH")]
234 file: Option<std::path::PathBuf>,
235 /// Overwrite existing config and hooks.
236 #[arg(short, long)]
237 force: bool,
238 /// Only set the filter config; don't install hooks.
239 #[arg(long)]
240 skip_repo: bool,
241 /// Configure the smudge filter to pass pointer text through
242 /// unchanged. Use with a follow-up `git lfs pull` to download
243 /// content on demand.
244 #[arg(long)]
245 skip_smudge: bool,
246 },
247 /// Reverse of `install`: clear the `filter.lfs.*` config and remove
248 /// the LFS git hooks. Hooks that don't match what we'd write are left
249 /// untouched.
250 Uninstall {
251 /// Optional mode: `hooks` removes only the LFS git hooks and
252 /// leaves the filter config alone (the inverse of `--skip-repo`).
253 mode: Option<String>,
254 /// Operate on the local repo only (default: --global).
255 #[arg(short, long)]
256 local: bool,
257 /// Operate on `/etc/gitconfig` (`git config --system`).
258 #[arg(long)]
259 system: bool,
260 /// Operate on `.git/config.worktree` for the current worktree.
261 #[arg(long)]
262 worktree: bool,
263 /// Operate on the given config file directly. Treated as
264 /// "global-like" for the success message.
265 #[arg(long, value_name = "PATH")]
266 file: Option<std::path::PathBuf>,
267 /// Only unset config; don't touch hooks.
268 #[arg(long)]
269 skip_repo: bool,
270 },
271 /// Track a file pattern with git-lfs by adding it to .gitattributes.
272 /// With no patterns, lists currently-tracked patterns.
273 Track {
274 /// File patterns to track (e.g. "*.jpg", "data/*.bin").
275 patterns: Vec<String>,
276 /// Mark the tracked pattern as `lockable` (`*.psd lockable`).
277 #[arg(short = 'l', long)]
278 lockable: bool,
279 /// Re-track an existing pattern, removing its `lockable` flag.
280 #[arg(long)]
281 not_lockable: bool,
282 /// Print what would happen without modifying `.gitattributes` or
283 /// re-staging files.
284 #[arg(long)]
285 dry_run: bool,
286 /// Extra logging: print "Found N files previously added to Git
287 /// matching pattern" lines.
288 #[arg(short, long)]
289 verbose: bool,
290 /// Listing mode only: emit JSON instead of the human-readable
291 /// listing.
292 #[arg(long)]
293 json: bool,
294 /// Listing mode only: suppress the "Listing excluded patterns"
295 /// section.
296 #[arg(long)]
297 no_excluded: bool,
298 /// Treat each pattern as a literal filename — escape glob
299 /// metacharacters (`*`, `?`, `[`, `]`, backslash, space) so
300 /// the entry in `.gitattributes` matches that exact name even
301 /// when it contains shell-glob characters.
302 #[arg(long)]
303 filename: bool,
304 /// Don't modify `.gitattributes` — the user has already added
305 /// the LFS filter line themselves. Still walks the index and
306 /// touches matching files' mtime so they show as modified on
307 /// the next `git status`.
308 #[arg(long)]
309 no_modify_attrs: bool,
310 },
311 /// Stop tracking a file pattern with git-lfs by removing it from
312 /// .gitattributes. The matching pointer files in history (and the
313 /// objects in the local store) are left in place.
314 Untrack {
315 /// File patterns to untrack.
316 patterns: Vec<String>,
317 },
318 /// Run the long-running filter-process protocol with git over stdin/stdout.
319 /// This is what git invokes via filter.lfs.process and is the batched
320 /// alternative to per-invocation `clean`/`smudge`.
321 FilterProcess {
322 /// Pass smudge requests' pointer text through unchanged;
323 /// equivalent to `GIT_LFS_SKIP_SMUDGE=1`. Wired up by
324 /// `install --skip-smudge`.
325 #[arg(long)]
326 skip: bool,
327 },
328 /// Download every LFS object reachable from the given refs (default: HEAD)
329 /// that isn't already in the local store. Walks history, dedupes by OID.
330 Fetch {
331 /// First positional arg is treated as a remote name (if it
332 /// resolves); subsequent args are refs.
333 args: Vec<String>,
334 /// List the objects that would be fetched without downloading
335 /// them (one `fetch <oid> => <path>` line per object).
336 #[arg(long)]
337 dry_run: bool,
338 /// JSON output. With `--dry-run`, queries the server's batch
339 /// endpoint to populate `actions` URLs.
340 #[arg(long)]
341 json: bool,
342 /// Walk every local ref under `refs/heads/*` + `refs/tags/*`.
343 #[arg(long)]
344 all: bool,
345 /// Re-download objects we already have (e.g. recovery from a
346 /// corrupt local store).
347 #[arg(long)]
348 refetch: bool,
349 /// Read refs from stdin, one per line. Blank lines dropped.
350 #[arg(long)]
351 stdin: bool,
352 /// Run `prune` after the fetch completes.
353 #[arg(long)]
354 prune: bool,
355 /// Comma-separated globs; only matching paths are fetched.
356 /// Falls back to `lfs.fetchinclude` when omitted.
357 #[arg(short = 'I', long)]
358 include: Vec<String>,
359 /// Comma-separated globs; matching paths are skipped. Falls
360 /// back to `lfs.fetchexclude` when omitted.
361 #[arg(short = 'X', long)]
362 exclude: Vec<String>,
363 },
364 /// `fetch` then re-run the smudge filter so the working tree contains
365 /// real LFS file contents instead of pointer text. Requires
366 /// `git lfs install` to have wired up the smudge filter.
367 Pull {
368 /// Refs to scan for LFS pointers. Defaults to `HEAD`.
369 refs: Vec<String>,
370 /// Comma-separated globs; only matching paths are pulled.
371 /// Falls back to `lfs.fetchinclude` when omitted.
372 #[arg(short = 'I', long)]
373 include: Vec<String>,
374 /// Comma-separated globs; matching paths are skipped. Falls
375 /// back to `lfs.fetchexclude` when omitted.
376 #[arg(short = 'X', long)]
377 exclude: Vec<String>,
378 },
379 /// Upload every LFS object reachable from the given refs that the
380 /// remote doesn't already have. The "doesn't have" set is approximated
381 /// by `refs/remotes/<remote>/*`; the LFS server's batch API also
382 /// dedupes server-side so missing exclusions don't waste bandwidth.
383 Push {
384 /// Name of the remote (e.g. "origin") whose tracking refs are
385 /// excluded from the upload set.
386 remote: String,
387 /// Refs (or, with `--object-id`, raw OIDs) to push. With
388 /// `--all`, restricts the all-refs walk to these; with
389 /// `--stdin`, ignored (a warning is emitted).
390 args: Vec<String>,
391 /// List the objects that would be pushed without actually
392 /// uploading them (one `push <oid> => <path>` line per object).
393 #[arg(long)]
394 dry_run: bool,
395 /// Push every local ref under `refs/heads/*` and `refs/tags/*`
396 /// (intersected with `args` if any are given).
397 #[arg(long)]
398 all: bool,
399 /// Read refs (or OIDs, with `--object-id`) from stdin, one per
400 /// line. Blank lines are skipped.
401 #[arg(long)]
402 stdin: bool,
403 /// Treat positional args / stdin entries as raw LFS OIDs
404 /// rather than git refs, and upload those objects directly
405 /// from the local store.
406 #[arg(long)]
407 object_id: bool,
408 },
409 /// Deprecated. Wraps `git clone` so the working tree is populated
410 /// with pointer text first, then runs `git lfs pull` to download
411 /// LFS content in batch. Modern `git clone` parallelizes the
412 /// smudge filter and is no slower; prefer it.
413 Clone {
414 /// `git clone` and LFS pass-through args. The repository URL
415 /// is required; an optional target directory follows.
416 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
417 args: Vec<String>,
418 },
419 /// Git post-checkout hook entry point. Receives `<prev-sha>
420 /// <post-sha> <flag>` (flag is "1" if HEAD moved). Currently a
421 /// no-op stub — exists so installed hook scripts don't fail. Real
422 /// behavior arrives with `track --lockable`.
423 PostCheckout { args: Vec<String> },
424 /// Git post-commit hook entry point. No arguments. Currently a
425 /// no-op stub.
426 PostCommit { args: Vec<String> },
427 /// Git post-merge hook entry point. Receives `<squash-flag>`.
428 /// Currently a no-op stub.
429 PostMerge { args: Vec<String> },
430 /// Git pre-push hook entry point — not typically invoked by hand.
431 /// Reads `<local-ref> <local-sha> <remote-ref> <remote-sha>` lines
432 /// from stdin and uploads the LFS objects newly reachable from each
433 /// `<local-sha>`.
434 PrePush {
435 /// Name of the remote being pushed to.
436 remote: String,
437 /// URL of the remote (informational; we use `lfs.url` config).
438 url: Option<String>,
439 /// List the objects that would be pushed without actually
440 /// uploading them.
441 #[arg(long)]
442 dry_run: bool,
443 },
444 /// Print the git-lfs version and exit.
445 Version,
446 /// Debug helper: build a pointer from a file, parse one from disk
447 /// or stdin, or just check whether some bytes are a valid pointer.
448 Pointer {
449 /// Build a pointer from this file (read content, hash, encode).
450 #[arg(short, long)]
451 file: Option<PathBuf>,
452 /// Parse and display this existing pointer file.
453 #[arg(short, long)]
454 pointer: Option<PathBuf>,
455 /// Read a pointer from stdin (mutually exclusive with --pointer).
456 #[arg(long)]
457 stdin: bool,
458 /// Validity check mode: exit 0 if input parses, 1 if not, 2 if
459 /// `--strict` and not byte-canonical.
460 #[arg(long)]
461 check: bool,
462 /// In `--check`, also reject non-canonical pointers.
463 #[arg(long)]
464 strict: bool,
465 /// Explicitly disable strict mode (paired with `--strict`).
466 #[arg(long)]
467 no_strict: bool,
468 },
469 /// Show the LFS environment: version, endpoints, on-disk paths, and
470 /// the three `filter.lfs.*` config values.
471 Env,
472 /// List the configured LFS pointer extensions (`lfs.extension.<name>.*`).
473 /// Extensions chain external clean/smudge programs around each LFS
474 /// object; this prints their resolved configuration in priority order.
475 Ext,
476 /// (Re-)install the four LFS git hooks (`pre-push`, `post-checkout`,
477 /// `post-commit`, `post-merge`) for the current repository.
478 Update {
479 /// Overwrite any custom hook contents.
480 #[arg(long)]
481 force: bool,
482 /// Print install instructions instead of writing the hook files.
483 #[arg(long)]
484 manual: bool,
485 },
486 /// Analyze or rewrite history for LFS conversion. Phase 1 ships
487 /// `info` only; `import` and `export` will land in subsequent phases.
488 Migrate {
489 #[command(subcommand)]
490 cmd: MigrateCmd,
491 },
492 /// Replace pointer text in the working tree with actual LFS object
493 /// content. With no args, materializes every LFS pointer in HEAD's
494 /// tree. With paths (literal file names or trailing-slash directory
495 /// prefixes), restricts to matching pointers.
496 ///
497 /// During a merge conflict, `--to <path> --ours/--theirs/--base
498 /// <file>` writes the LFS content from one of the conflicted
499 /// stages to `<path>` (creating intermediate directories) so the
500 /// user can compare or salvage versions.
501 Checkout {
502 /// Paths to check out. Empty = everything in HEAD's tree.
503 /// In conflict mode (`--to`), exactly one path is required.
504 paths: Vec<String>,
505 /// Conflict-mode: write the chosen stage's content to this
506 /// path instead of into the working tree. Resolves relative
507 /// to the current directory.
508 #[arg(long, value_name = "PATH")]
509 to: Option<String>,
510 /// Conflict-mode: pull from stage 2 (HEAD's version). Mutually
511 /// exclusive with `--theirs` and `--base`.
512 #[arg(long)]
513 ours: bool,
514 /// Conflict-mode: pull from stage 3 (the merging-in version).
515 #[arg(long)]
516 theirs: bool,
517 /// Conflict-mode: pull from stage 1 (the common ancestor).
518 #[arg(long)]
519 base: bool,
520 },
521 /// Delete local LFS objects that aren't reachable from HEAD or any
522 /// unpushed commit. Reclaims disk for repos whose history has moved
523 /// past their objects.
524 Prune {
525 /// Don't delete anything; just report what would go.
526 #[arg(short, long)]
527 dry_run: bool,
528 /// Print each prunable object's OID and size.
529 #[arg(short, long)]
530 verbose: bool,
531 },
532 /// Check the integrity of LFS objects and pointers reachable from
533 /// `<refspec>` (default: HEAD). Exit 1 if anything is corrupt.
534 Fsck {
535 /// Ref to scan. Defaults to HEAD.
536 refspec: Option<String>,
537 /// Only check objects (verify store contents match pointer OIDs).
538 #[arg(long)]
539 objects: bool,
540 /// Only check pointers (flag non-canonical pointer encodings).
541 #[arg(long)]
542 pointers: bool,
543 /// Report problems but don't move corrupt objects to `<lfs>/bad/`.
544 #[arg(short, long)]
545 dry_run: bool,
546 },
547 /// Show staged + unstaged changes, classifying each blob as LFS,
548 /// Git, or working-tree File.
549 Status {
550 /// Stable one-line-per-change format for scripts.
551 #[arg(short, long)]
552 porcelain: bool,
553 /// Stable JSON output for scripts; only LFS entries are reported.
554 #[arg(short, long)]
555 json: bool,
556 },
557 /// Acquire an exclusive server-side lock on one or more files.
558 /// Other users will be unable to push changes to a locked file.
559 Lock {
560 /// Paths to lock (repo-relative or absolute, must resolve inside
561 /// the working tree).
562 paths: Vec<String>,
563 /// Specify which remote to use when interacting with locks.
564 #[arg(short, long)]
565 remote: Option<String>,
566 /// Refspec to associate the lock with. Defaults to the current
567 /// branch's tracked upstream (`branch.<current>.merge`) or the
568 /// current branch's full ref (`refs/heads/<branch>`).
569 #[arg(long = "ref")]
570 refspec: Option<String>,
571 /// Stable JSON output for scripts.
572 #[arg(short, long)]
573 json: bool,
574 },
575 /// List file locks held on the server.
576 Locks {
577 /// Specify which remote to use when interacting with locks.
578 #[arg(short, long)]
579 remote: Option<String>,
580 /// Filter results to a particular path.
581 #[arg(short, long)]
582 path: Option<String>,
583 /// Filter results to a particular lock id.
584 #[arg(short, long)]
585 id: Option<String>,
586 /// Maximum number of results to return.
587 #[arg(short, long)]
588 limit: Option<u32>,
589 /// Refspec to filter locks by (defaults to current branch /
590 /// tracked upstream — same auto-resolution as `git lfs lock`).
591 #[arg(long = "ref")]
592 refspec: Option<String>,
593 /// Verify ownership: prefix locks owned by the authenticated user
594 /// with `O ` (others get ` `).
595 #[arg(long)]
596 verify: bool,
597 /// List from the on-disk cache of own locks instead of querying
598 /// the server. Combine with `--path` / `--id` / `--limit` to
599 /// filter; `--verify` is rejected. Useful when offline or to
600 /// confirm what `git lfs lock` recorded locally.
601 #[arg(long)]
602 local: bool,
603 /// Stable JSON output for scripts.
604 #[arg(short, long)]
605 json: bool,
606 },
607 /// Release a file lock previously acquired with `git lfs lock`.
608 /// Either provide one or more paths, or `--id <id>` (mutually
609 /// exclusive).
610 Unlock {
611 /// Paths to unlock; mutually exclusive with `--id`.
612 paths: Vec<String>,
613 /// Lock id to release; mutually exclusive with paths.
614 #[arg(short, long)]
615 id: Option<String>,
616 /// Forcibly break another user's lock(s).
617 #[arg(short, long)]
618 force: bool,
619 /// Specify which remote to use when interacting with locks.
620 #[arg(short, long)]
621 remote: Option<String>,
622 /// Refspec to send with the unlock request (defaults to current
623 /// branch / tracked upstream).
624 #[arg(long = "ref")]
625 refspec: Option<String>,
626 /// Stable JSON output for scripts.
627 #[arg(short, long)]
628 json: bool,
629 },
630 /// List LFS-tracked files visible at a ref (default: HEAD), or across
631 /// all reachable history with `--all`.
632 LsFiles {
633 /// Ref to list. Defaults to HEAD.
634 refspec: Option<String>,
635 /// Show full 64-char OID instead of the 10-char prefix.
636 #[arg(short, long)]
637 long: bool,
638 /// Append humanized size in parens.
639 #[arg(short, long)]
640 size: bool,
641 /// Print only the path.
642 #[arg(short, long)]
643 name_only: bool,
644 /// Walk every reachable ref's full history.
645 #[arg(short, long)]
646 all: bool,
647 /// Multi-line per-file block (size, checkout, download, oid, version).
648 #[arg(short, long)]
649 debug: bool,
650 /// Stable JSON output for scripts.
651 #[arg(short, long)]
652 json: bool,
653 },
654}