Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

git-lfs, implemented in Rust. A from-scratch port of Git LFS, aiming for feature parity with the upstream Go binary at the CLI and wire-protocol level — with a cleaner library split and friendlier help output along the way.

These docs are split into three parts. Protocol & format mirrors the vendored upstream specs (pointer files, batch API, locking, custom transfer adapters, extensions). Commands and Plumbing are the per-command reference. Hooks documents the post-* / pre-push shims that git lfs install drops into .git/hooks/.

The implementation is still in progress; see the project README for an up-to-date snapshot of what works.

Git is a trademark of the Software Freedom Conservancy.

Installation

git-lfs ships three ways: as native packages for the major Linux distributions, as a Homebrew tap (Linux + macOS), and as a Rust crate on crates.io. Pick the one that matches your platform.

Homebrew (Linux and macOS)

brew tap rustutils/tap
brew install rustutils/tap/git-lfs

APT (Debian and Ubuntu)

Add the signing key, register the repository, and install:

sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSLo /etc/apt/keyrings/rustutils.asc https://rustutils.gitlab.io/apt/rustutils.asc
echo "deb [signed-by=/etc/apt/keyrings/rustutils.asc] https://rustutils.gitlab.io/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/rustutils.list
sudo apt update
sudo apt install git-lfs-rs

The package is named git-lfs-rs to avoid colliding with the upstream Go git-lfs package in Debian. The binary is still called git-lfs, and installing it will replace git-lfs if you have it installed (but you can always install git-lfs to go back to it).

RPM (Fedora, RHEL, Rocky, AlmaLinux)

sudo curl -fsSLo /etc/yum.repos.d/rustutils.repo https://rustutils.gitlab.io/rpm/rustutils.repo
sudo dnf install git-lfs-rs

Same git-lfs-rs naming convention.

Cargo (any platform with a Rust toolchain)

cargo install git-lfs

This drops a git-lfs binary into ~/.cargo/bin/. Make sure that directory is on your PATH so git can find the executable when it shells out to invoke filters and hooks.

After installing

Run once per machine to register the clean, smudge, and process filters in your global git config:

git lfs install

From there on, git lfs <command> works in any repo with a .gitattributes that tracks files via LFS. Per-repository hook installation happens automatically the first time you run an LFS-aware command in a new clone, no need to re-run git lfs install per repo.

To enable Git LFS in a repository, you can tell it to track specific files. For example:

# track all PDF files in this repository
git lfs track *.pdf
# track all JPEG images in this repository
git lfs track *.jpg

Git LFS Specification

This is a general guide for Git LFS clients. Typically it should be implemented by a command line git-lfs tool, but the details may be useful for other tools.

The Pointer

The core Git LFS idea is that instead of writing large blobs to a Git repository, only a pointer file is written.

  • Pointer files are text files which MUST contain only UTF-8 characters.
  • Each line MUST be of the format {key} {value}\n (trailing unix newline).
  • Only a single space character between {key} and {value}.
  • Keys MUST only use the characters [a-z] [0-9] . -.
  • The first key is always version.
  • Lines of key/value pairs MUST be sorted alphabetically in ascending order (with the exception of version, which is always first).
  • Values MUST NOT contain return or newline characters.
  • Pointer files MUST be stored in Git with their executable bit matching that of the replaced file.
  • Pointer files must be less than 1024 bytes in size, including any pointer extension lines.
  • Pointer files are unique: that is, there is exactly one valid encoding for a pointer file.

An empty file is the pointer for an empty file. That is, empty files are passed through LFS without any change.

The required keys are:

  • version is a URL that identifies the pointer file spec. Parsers MUST use simple string comparison on the version, without any URL parsing or normalization. It is case sensitive, and %-encoding is discouraged.
  • oid tracks the unique object id for the file, prefixed by its hashing method: {hash-method}:{hash}. Currently, only sha256 is supported. The hash is lower case hexadecimal.
  • size is in bytes.

Example of a v1 text pointer:

version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
(ending \n)

Blobs created with the pre-release version of the tool generated files with a different version URL. Git LFS can read these files, but writes them using the version URL above.

version https://hawser.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
(ending \n)

For testing compliance of any tool generating its own pointer files, the reference is this official Git LFS tool:

NOTE: exact pointer command behavior TBD!

  • Tools that parse and regenerate pointer files MUST preserve keys that they don’t know or care about.

  • Run the pointer command to generate a pointer file for the given local file:

    $ git lfs pointer --file=path/to/file
    Git LFS pointer for path/to/file:
    
    version https://git-lfs.github.com/spec/v1
    oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
    size 12345
    
  • Run pointer to compare the blob OID of a pointer file built by Git LFS with a pointer built by another tool.

    • Write the other implementation’s pointer to “other/pointer/file”:

      $ git lfs pointer --file=path/to/file --pointer=other/pointer/file
      Git LFS pointer for path/to/file:
      
      version https://git-lfs.github.com/spec/v1
      oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
      size 12345
      
      Blob OID: 60c8d8ab2adcf57a391163a7eeb0cdb8bf348e44
      
      Pointer from other/pointer/file
      version https://git-lfs.github.com/spec/v1
      oid sha256 4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
      size 12345
      
      Blob OID: 08e593eeaa1b6032e971684825b4b60517e0638d
      
      Pointers do not match
      
    • It can also read STDIN to get the other implementation’s pointer:

      $ cat other/pointer/file | git lfs pointer --file=path/to/file --stdin
      Git LFS pointer for path/to/file:
      
      version https://git-lfs.github.com/spec/v1
      oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
      size 12345
      
      Blob OID: 60c8d8ab2adcf57a391163a7eeb0cdb8bf348e44
      
      Pointer from STDIN
      version https://git-lfs.github.com/spec/v1
      oid sha256 4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
      size 12345
      
      Blob OID: 08e593eeaa1b6032e971684825b4b60517e0638d
      
      Pointers do not match
      

Intercepting Git

Git LFS uses the clean and smudge filters to decide which files use it. The global filters can be set up with git lfs install:

$ git lfs install

These filters ensure that large files aren’t written into the repository proper, instead being stored locally at .git/lfs/objects/{OID-PATH} (where {OID-PATH} is a sharded filepath of the form OID[0:2]/OID[2:4]/OID), synchronized with the Git LFS server as necessary. Here is a sample path to a file:

.git/lfs/objects/4d/7a/4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393

The clean filter runs as files are added to repositories. Git sends the content of the file being added as STDIN, and expects the content to write to Git as STDOUT.

  • Stream binary content from STDIN to a temp file, while calculating its SHA-256 signature.
  • Atomically move the temp file to .git/lfs/objects/{OID-PATH} if it does not exist, and the sha-256 signature of the contents matches the given OID.
  • Delete the temp file.
  • Write the pointer file to STDOUT.

Note that the clean filter does not push the file to the server. Use the git push command to do that (lfs files are pushed before commits in a pre-push hook).

The smudge filter runs as files are being checked out from the Git repository to the working directory. Git sends the content of the Git blob as STDIN, and expects the content to write to the working directory as STDOUT.

  • Read 100 bytes.
  • If the content is ASCII and matches the pointer file format:
    • Look for the file in .git/lfs/objects/{OID-PATH}.
    • If it’s not there, download it from the server.
    • Write its contents to STDOUT
  • Otherwise, simply pass the STDIN out through STDOUT.

The .gitattributes file controls when the filters run. Here’s a sample file that runs all mp3 and zip files through Git LFS:

$ cat .gitattributes
*.mp3 filter=lfs -text
*.zip filter=lfs -text

Use the git lfs track command to view and add to .gitattributes.

Adding Custom Transfer Agents to LFS

Introduction

Git LFS supports multiple ways to transfer (upload and download) files. In the core client, the basic way to do this is via a one-off HTTP request via the URL returned from the LFS API for a given object. The core client also supports extensions to allow resuming of downloads (via Range headers) and uploads (via the tus.io protocol).

Some people might want to be able to transfer content in other ways, however. To enable this, git-lfs allows configuring Custom Transfers, which are simply processes which must adhere to the protocol defined later in this document. git-lfs will invoke the process at the start of all transfers, and will communicate with the process via stdin/stdout for each transfer.

Custom Transfer Type Selection

In the LFS API request, the client includes a list of transfer types it supports. When replying, the API server will pick one of these and make any necessary adjustments to the returned object actions, in case the picked transfer type needs custom details about how to do each transfer.

Using a Custom Transfer Type without the API server

In some cases the transfer agent can figure out by itself how and where the transfers should be made, without having to query the API server. In this case it’s possible to use the custom transfer agent directly, without querying the server, by using the following config option:

  • lfs.standalonetransferagent, lfs.<url>.standalonetransferagent

    Specifies a custom transfer agent to be used if the API server URL matches as in git config --get-urlmatch lfs.standalonetransferagent <apiurl>. git-lfs will not contact the API server. It instead sets stage 2 transfer actions to null. lfs.<url>.standalonetransferagent can be used to configure a custom transfer agent for individual remotes. lfs.standalonetransferagent unconditionally configures a custom transfer agent for all remotes. The custom transfer agent must be specified in a lfs.customtransfer.<name> settings group.

Defining a Custom Transfer Type

A custom transfer process is defined under a settings group called lfs.customtransfer.<name>, where <name> is an identifier (see Naming below).

  • lfs.customtransfer.<name>.path

    path should point to the process you wish to invoke. This will be invoked at the start of all transfers (possibly many times, see the concurrent option below) and the protocol over stdin/stdout is defined below in the Protocol section.

  • lfs.customtransfer.<name>.args

    If the custom transfer process requires any arguments, these can be provided here. Typically you would only need this if your process was multi-purpose or particularly flexible, most of the time you won’t need it. Note that this string will be expanded by the shell.

  • lfs.customtransfer.<name>.concurrent

    If true (the default), git-lfs will invoke the custom transfer process multiple times in parallel, according to lfs.concurrenttransfers, splitting the transfer workload between the processes.

    If you would prefer that only one instance of the transfer process is invoked, maybe because you want to do your own parallelism internally (e.g. slicing files into parts), set this to false.

  • lfs.customtransfer.<name>.direction

    Specifies which direction the custom transfer process supports, either download, upload, or both. The default if unspecified is both.

Naming

Each custom transfer must have a name which is unique to the underlying mechanism, and the client and the server must agree on that name. The client will advertise this name to the server as a supported transfer approach, and if the server supports it, it will return relevant object action links. Because these may be very different from standard HTTP URLs it’s important that the client and server agree on the name.

For example, let’s say I’ve implemented a custom transfer process which uses NFS. I could call this transfer type nfs - although it’s not specific to my configuration exactly, it is specific to the way NFS works, and the server will need to give me different URLs. Assuming I define my transfer like this, and the server supports it, I might start getting object action links back like nfs://<host>/path/to/object

Protocol

The git-lfs client communicates with the custom transfer process via the stdin and stdout streams. No file content is communicated on these streams, only request / response metadata. The metadata exchanged is always in JSON format. External files will be referenced when actual content is exchanged.

Line Delimited JSON

Because multiple JSON messages will be exchanged on the same stream it’s useful to delimit them explicitly rather than have the parser find the closing } in an arbitrary stream, therefore each JSON structure will be sent and received on a single line as per Line Delimited JSON.

In other words when git-lfs sends a JSON message to the custom transfer it will be on a single line, with a line feed at the end. The transfer process must respond the same way by writing a JSON structure back to stdout with a single line feed at the end (and flush the output).

Protocol Stages

The protocol consists of 3 stages:

Stage 1: Initiation

Immediately after invoking a custom transfer process, git-lfs sends initiation data to the process over stdin. This tells the process useful information about the configuration.

The message will look like this:

{ "event": "init", "operation": "download", "remote": "origin", "concurrent": true, "concurrenttransfers": 3 }
  • event: Always init to identify this message
  • operation: will be upload or download depending on transfer direction
  • remote: The Git remote. It can be a remote name like origin or an URL like ssh://git.example.com//path/to/repo. A standalone transfer agent can use it to determine the location of remote files.
  • concurrent: reflects the value of lfs.customtransfer.<name>.concurrent, in case the process needs to know
  • concurrenttransfers: reflects the value of lfs.concurrenttransfers, for if the transfer process wants to implement its own concurrency and wants to respect this setting.

The transfer process should use the information it needs from the initiation structure, and also perform any one-off setup tasks it needs to do. It should then respond on stdout with a simple empty confirmation structure, as follows:

{ }

Or if there was an error:

{ "error": { "code": 32, "message": "Some init failure message" } }

Stage 2: 0..N Transfers

After the initiation exchange, git-lfs will send any number of transfer requests to the stdin of the transfer process, in a serial sequence. Once a transfer request is sent to the process, it awaits a completion response before sending the next request.

Uploads

For uploads the request sent from git-lfs to the transfer process will look like this:

{ "event": "upload", "oid": "bf3e3e2af9366a3b704ae0c31de5afa64193ebabffde2091936ad2e7510bc03a", "size": 346232, "path": "/path/to/file.png", "action": { "href": "nfs://server/path", "header": { "key": "value" } } }
  • event: Always upload to identify this message
  • oid: the identifier of the LFS object
  • size: the size of the LFS object
  • path: the file which the transfer process should read the upload data from
  • action: the upload action copied from the response from the batch API. This contains href and header contents, which are named per HTTP conventions, but can be interpreted however the custom transfer agent wishes (this is an NFS example, but it doesn’t even have to be an URL). Generally, href will give the primary connection details, with header containing any miscellaneous information needed. action is null for standalone transfer agents.

The transfer process should post one or more progress messages and then a final completion message as follows:

{ "event": "complete", "oid": "bf3e3e2af9366a3b704ae0c31de5afa64193ebabffde2091936ad2e7510bc03a" }
  • event: Always complete to identify this message
  • oid: the identifier of the LFS object

Or if there was an error in the transfer:

{ "event": "complete", "oid": "bf3e3e2af9366a3b704ae0c31de5afa64193ebabffde2091936ad2e7510bc03a", "error": { "code": 2, "message": "Explain what happened to this transfer" } }
  • event: Always complete to identify this message
  • oid: the identifier of the LFS object
  • error: Should contain a code and message explaining the error
Downloads

For downloads the request sent from git-lfs to the transfer process will look like this:

{ "event": "download", "oid": "22ab5f63670800cc7be06dbed816012b0dc411e774754c7579467d2536a9cf3e", "size": 21245, "action": { "href": "nfs://server/path", "header": { "key": "value" } } }
  • event: Always download to identify this message
  • oid: the identifier of the LFS object
  • size: the size of the LFS object
  • action: the download action copied from the response from the batch API. This contains href and header contents, which are named per HTTP conventions, but can be interpreted however the custom transfer agent wishes (this is an NFS example, but it doesn’t even have to be an URL). Generally, href will give the primary connection details, with header containing any miscellaneous information needed. action is null for standalone transfer agents.

Note there is no file path included in the download request; the transfer process should create a file itself and return the path in the final response after completion (see below).

The transfer process should post one or more progress messages and then a final completion message as follows:

{ "event": "complete", "oid": "22ab5f63670800cc7be06dbed816012b0dc411e774754c7579467d2536a9cf3e", "path": "/path/to/file.png" }
  • event: Always complete to identify this message
  • oid: the identifier of the LFS object
  • path: the path to a file containing the downloaded data, which the transfer process relinquishes control of to git-lfs. git-lfs will move the file into LFS storage.

Or, if there was a failure transferring this item:

{ "event": "complete", "oid": "22ab5f63670800cc7be06dbed816012b0dc411e774754c7579467d2536a9cf3e", "error": { "code": 2, "message": "Explain what happened to this transfer" } }
  • event: Always complete to identify this message
  • oid: the identifier of the LFS object
  • error: Should contain a code and message explaining the error

Errors for a single transfer request should not terminate the process. The error should be returned in the response structure instead.

The custom transfer adapter does not need to check the SHA of the file content it has downloaded, git-lfs will do that before moving the final content into the LFS store.

Progress

In order to support progress reporting while data is uploading / downloading, the transfer process should post messages to stdout as follows before sending the final completion message:

{ "event": "progress", "oid": "22ab5f63670800cc7be06dbed816012b0dc411e774754c7579467d2536a9cf3e", "bytesSoFar": 1234, "bytesSinceLast": 64 }
  • event: Always progress to identify this message
  • oid: the identifier of the LFS object
  • bytesSoFar: the total number of bytes transferred so far
  • bytesSinceLast: the number of bytes transferred since the last progress message

The transfer process should post these messages such that the last one sent has bytesSoFar equal to the file size on success.

Stage 3: Finish & Cleanup

When all transfers have been processed, git-lfs will send the following message to the stdin of the transfer process:

{ "event": "terminate" }

On receiving this message the transfer process should clean up and terminate. No response is expected.

Error handling

Any unexpected fatal errors in the transfer process (not errors specific to a transfer request) should set the exit code to non-zero and print information to stderr. Otherwise the exit code should be 0 even if some transfers failed.

A Note On Verify Actions

You may have noticed that that only the upload and download actions are passed to the custom transfer agent for processing, what about the verify action, if the API returns one?

Custom transfer agents do not handle the verification process, only the upload and download of content. The verify link is typically used to notify a system other than the actual content store after an upload was completed, therefore it makes more sense for that to be handled via the normal API process.

Extending LFS

Teams who use Git LFS often have custom requirements for how the pointer files and blobs should be handled. Some examples of extensions that could be built:

  • Compress large files on clean, uncompress them on smudge/fetch
  • Encrypt files on clean, decrypt on smudge/fetch
  • Scan files on clean to make sure they don’t contain sensitive information

The basic extensibility model is that LFS extensions must be registered explicitly, and they will be invoked on clean and smudge to manipulate the contents of the files as needed. On clean, LFS itself ensures that the pointer file is updated with all the information needed to be able to smudge correctly, and the extensions never modify the pointer file directly.

NOTE: This feature is considered experimental, and included so developers can work on extensions. Exact details of how extensions work are subject to change based on feedback. It is possible for buggy extensions to leave your repository in a bad state, so don’t rely on them with a production git repository without extensive testing.

Registration

To register an LFS extension, it must be added to the Git config. Each extension needs to define:

  • Its unique name. This will be used as part of the key in the pointer file.
  • The command to run on clean (when files are added to git).
  • The command to run on smudge (when files are downloaded and checked out).
  • The priority of the extension, which must be a unique, non-negative integer.

The sequence %f in the clean and smudge commands will be replaced by the filename being processed.

Here’s an example extension registration in the Git config:

[lfs "extension.foo"]
  clean = foo clean %f
  smudge = foo smudge %f
  priority = 0
[lfs "extension.bar"]
  clean = bar clean %f
  smudge = bar smudge %f
  priority = 1

Clean

When staging a file, Git invokes the LFS clean filter, as described earlier. If no extensions are installed, the LFS clean filter reads bytes from STDIN, calculates the SHA-256 signature, and writes the bytes to a temp file. It then moves the temp file into the appropriate place in .git/lfs/objects and writes a valid pointer file to STDOUT.

When an extension is installed, LFS will invoke the extension to do additional processing on the bytes before writing them into the temp file. If multiple extensions are installed, they are invoked in the order defined by their priority. LFS will also insert a key in the pointer file for each extension that was invoked, indicating both the order that the extension was invoked and the oid of the file before that extension was invoked. All of that information is required to be able to reliably smudge the file later. Each new line in the pointer file will be of the form:

ext-{order}-{name} {hash-method}:{hash-of-input-to-extension}

This naming ensures that all extensions are written in both alphabetical and priority order, and also shows the progression of changes to the oid as it is processed by the extensions.

Here’s an example sequence, assuming extensions foo and bar are installed, as shown in the previous section.

  • Git passes the original contents of the file to LFS clean over STDIN.
  • LFS reads those bytes and calculates the original SHA-256 signature.
  • LFS streams the bytes to STDIN of foo clean, which is expected to write those bytes, modified or not, to its STDOUT.
  • LFS reads the bytes from STDOUT of foo clean, calculates the SHA-256 signature, and writes them to STDIN of bar clean, which then writes those bytes, modified or not, to its STDOUT.
  • LFS reads the bytes from STDOUT of bar clean, calculates the SHA-256 signature, and writes the bytes to a temp file.
  • When finished, LFS atomically moves the temp file into .git/lfs/objects.
  • LFS generates the pointer file, with some changes:
  • The oid and size keys are calculated from the final bytes written to LFS local storage.
  • LFS also writes keys named ext-0-foo and ext-1-bar into the pointer, along with their respective input oids.

Here’s an example pointer file, for a file processed by extensions foo and bar:

version https://git-lfs.github.com/spec/v1
ext-0-foo sha256:{original hash}
ext-1-bar sha256:{hash after foo}
oid sha256:{hash after bar}
size 123
(ending \n)

Note: as an optimization, if an extension just does a pass-through, its key can be omitted from the pointer file. This will make smudging the file a bit more efficient since that extension can be skipped. LFS can detect a pass-through extension because the input and output oids will be the same.

This implies that extensions must have no side effects other than writing to their STDOUT. Otherwise LFS has no way to know what extensions modified a file.

Smudge

When a file is checked out, Git invokes the LFS smudge filter, as described earlier. If no extensions are installed, the LFS smudge filter inspects the first 100 bytes of the bytes off STDIN, and if it is a pointer file, uses the oid to find the correct object in the LFS storage, and writes those bytes to STDOUT so that Git can write them to the working directory.

If the pointer file indicates that extensions were invoked on that file, then those extensions must be installed in order to smudge. If they are not installed, not found, or unusable for any reason, LFS will fail to smudge the file, and outputs an error indicating which extension is missing.

Each of the extensions indicated in the pointer file must be invoked in reverse order to undo the changes they made to the contents of the file. After each extension is invoked, LFS will compare the SHA-256 signature of the bytes output by the extension with the oid stored in the pointer file as the original input to that same extension. Those signatures must match, otherwise the extension did not undo its changes correctly. In that case, LFS fails to smudge the file, and outputs an error indicating which extension is failing.

Here’s an example sequence, indicating how LFS will smudge the pointer file shown in the previous section:

  • Git passes the bytes of the pointer file to LFS smudge over STDIN. Note that when using git lfs checkout, LFS reads the files directly from disk rather than off STDIN. The rest of the steps are unaffected either way.
  • LFS reads those bytes and inspects them to see if this is a pointer file. If it was not, the bytes would just be passed through to STDOUT.
  • Since it is a pointer file, LFS reads the whole file off STDIN, parses it, and determines that extensions foo and bar both processed the file, in that order.
  • LFS uses the value of the oid key to find the blob in the .git/lfs/objects folder, or download from the server as needed.
  • LFS writes the contents of the blob to STDIN of bar smudge, which modifies them as needed and writes them to its STDOUT.
  • LFS reads the bytes from STDOUT of bar smudge, calculates the SHA-256 signature, and writes the bytes to STDIN of foo smudge, which modifies them as needed and writes to them its STDOUT.
  • LFS reads the bytes from STDOUT of foo smudge, calculates the SHA-256 signature, and writes the bytes to its own STDOUT.
  • At the end, ensure that the hashes calculated on the outputs of foo and bar match their corresponding input hashes from the pointer file. If not, write a descriptive error message indicating which extension failed to undo its changes.
  • Question: On error, should we overwrite the file in the working directory with the original pointer file? Can this be done reliably?

Handling errors

If there are errors in the configuration of LFS extensions, such as invalid extension names, duplicate priorities, etc, then any LFS commands that rely on them will abort with a descriptive error message.

If an extension is unable to perform its task, it can indicate this error by returning a non-zero error code and writing a descriptive error message to its STDERR. The behavior on an error depends on whether we are cleaning or smudging.

Clean

If an extension fails to clean a file, it will return a non-zero error code and write an error message to its STDERR. Because the file was not cleaned correctly, it can’t be added to the index. LFS will ensure that no pointer file is added or updated for failed files. In addition, it will display the error messages for any files that could not be cleaned (and keep those errors in a log), so that the user can diagnose the failure, and then rerun “git add” on those files.

Smudge

If an extension fails to smudge a file, it will return a non-zero error code and write an error message to its STDERR. Because the file was not smudged correctly, LFS cannot update that file in the working directory. LFS will ensure that the pointer file is written to both the index and working directory. In addition, it will display the error messages for any files that could not be smudged (and keep those errors in a log), so that the user can diagnose the failure and then rerun git-lfs checkout to fix up any remaining pointer files.

Git LFS API

The Git LFS client uses an HTTPS server to coordinate fetching and storing large binary objects separately from a Git server. The basic process the client goes through looks like this:

  1. Discover the LFS Server to use.
  2. Apply Authentication.
  3. Make the request. See the Batch and File Locking API sections.

Batch API

The Batch API is used to request the ability to transfer LFS objects with the LFS server.

API Specification:

Current transfer adapters include:

Experimental transfer adapters include:

File Locking API

The File Locking API is used to create, list, and delete locks, as well as verify that locks are respected in Git pushes.

API Specification:

Server Discovery

One of the Git LFS goals is to work with supporting Git remotes with as few required configuration properties as possible. Git LFS will attempt to use your Git remote to determine the LFS server. You can also configure a custom LFS server if your Git remote doesn’t support one, or you just want to use a separate one.

Look for the Endpoint properties in git lfs env to see your current LFS servers.

Guessing the Server

By default, Git LFS will append .git/info/lfs to the end of a Git remote url to build the LFS server URL it will use:

Git Remote: https://git-server.com/foo/bar
LFS Server: https://git-server.com/foo/bar.git/info/lfs

Git Remote: https://git-server.com/foo/bar.git
LFS Server: https://git-server.com/foo/bar.git/info/lfs

Git Remote: git@git-server.com:foo/bar.git
LFS Server: https://git-server.com/foo/bar.git/info/lfs

Git Remote: ssh://git-server.com/foo/bar.git
LFS Server: https://git-server.com/foo/bar.git/info/lfs

SSH

If Git LFS detects an SSH remote, it will run the git-lfs-authenticate command. This allows supporting Git servers to give the Git LFS client alternative authentication so the user does not have to setup a git credential helper.

Git LFS runs the following command:

$ ssh [{user}@]{server} git-lfs-authenticate {path} {operation}

The user, server, and path properties are taken from the SSH remote. The operation can either be “download” or “upload”. The SSH command can be tweaked with the GIT_SSH or GIT_SSH_COMMAND environment variables. The output for successful commands is JSON, and matches the schema as an action in a Batch API response. Git LFS will dump the STDERR from the ssh command if it returns a non-zero exit code.

Examples:

The git-lfs-authenticate command can even suggest an LFS endpoint that does not match the Git remote by specifying an href property.

# Called for remotes like:
#   * git@git-server.com:foo/bar.git
#   * ssh://git@git-server.com/foo/bar.git
$ ssh git@git-server.com git-lfs-authenticate foo/bar.git download
{
  "href": "https://lfs-server.com/foo/bar",
  "header": {
    "Authorization": "RemoteAuth some-token"
  },
  "expires_in": 86400
}

Git LFS will output the STDERR if git-lfs-authenticate returns a non-zero exit code:

$ ssh git@git-server.com git-lfs-authenticate foo/bar.git wat
Invalid LFS operation: "wat"

Custom Configuration

If Git LFS can’t guess your LFS server, or you aren’t using the git-lfs-authenticate command, you can specify the LFS server using Git config.

Set lfs.url to set the LFS server, regardless of Git remote.

$ git config lfs.url https://lfs-server.com/foo/bar

You can set remote.{name}.lfsurl to set the LFS server for that specific remote only:

$ git config remote.dev.lfsurl http://lfs-server.dev/foo/bar
$ git lfs env
...

Endpoint=https://git-server.com/foo/bar.git/info/lfs (auth=none)
Endpoint (dev)=http://lfs-server.dev/foo/bar (auth=none)

Git LFS will also read these settings from a .lfsconfig file in the root of your repository. This lets you commit it to the repository so that all users can use it, if you wish.

$ git config --file=.lfsconfig lfs.url https://lfs-server.com/foo/bar

Authentication

The Git LFS API uses HTTP Basic Authentication to authorize requests. Therefore, HTTPS is strongly encouraged for all production Git LFS servers. The credentials can come from the following places:

SSH

Git LFS will add any HTTP headers returned from the git-lfs-authenticate command to any Batch API requests. If servers are returning expiring tokens, they can add an expires_in (or expires_at) property to hint when the token will expire.

# Called for remotes like:
#   * git@git-server.com:foo/bar.git
#   * ssh://git@git-server.com/foo/bar.git
$ ssh git@git-server.com git-lfs-authenticate foo/bar.git download
{
  "header": {
    "Authorization": "RemoteAuth some-token"
  },

  # optional, for expiring tokens, preferred over expires_at
  "expires_in": 86400,

  # optional, for expiring tokens
  "expires_at": "2016-11-10T15:29:07Z"
}

See the SSH section in the Server Discovery doc for more info about git-lfs-authenticate.

Git Credentials

Git provides a credentials command for storing and retrieving credentials through a customizable credential helper. By default, it associates the credentials with a domain. You can enable credential.useHttpPath so different repository paths have different credentials.

Git ships with a really basic credential cacher that stores passwords in memory, so you don’t have to enter your password frequently. However, you are encouraged to setup a custom git credential cacher, if a better one exists for your platform.

As of version 3.0, Git LFS no longer supports NTLM. Users are encouraged to set up Kerberos; for example, Azure DevOps Server recommends Kerberos over NTLM in this blog post. For pre-3.0 LFS versions, if your Git LFS server authenticates with NTLM then you must provide your credentials to git-credential in the form username:DOMAIN\user password:password.

Specified in URL

You can hardcode credentials into your Git remote or LFS url properties in your git config. This is not recommended for security reasons because it relies on the credentials living in your local git config.

$ git remote add origin https://user:password@git-server.com/foo/bar.git

Git LFS Batch API

Added: v0.6

The Batch API is used to request the ability to transfer LFS objects with the LFS server. The Batch URL is built by adding /objects/batch to the LFS server URL.

Git remote: https://git-server.com/foo/bar LFS server: https://git-server.com/foo/bar.git/info/lfs Batch API: https://git-server.com/foo/bar.git/info/lfs/objects/batch

See the Server Discovery doc for more info on how LFS builds the LFS server URL.

All Batch API requests use the POST verb, and require the following HTTP headers. The request and response bodies are JSON.

Accept: application/vnd.git-lfs+json
Content-Type: application/vnd.git-lfs+json

The client may also include a charset=utf-8 parameter in the Content-Type header, which servers should be prepared to accept.

See the Authentication doc for more info on how LFS gets authorizes Batch API requests.

Requests

The client sends the following information to the Batch endpoint to transfer some objects:

  • operation - Should be download or upload.
  • transfers - An optional Array of String identifiers for transfer adapters that the client has configured. If omitted, the basic transfer adapter MUST be assumed by the server.
  • ref - Optional object describing the server ref that the objects belong to. Note: Added in v2.4.
    • name - Fully-qualified server refspec.
  • objects - An Array of objects to download.
    • oid - String OID of the LFS object.
    • size - Integer byte size of the LFS object. Must be at least zero.
  • hash_algo - The hash algorithm used to name Git LFS objects. Optional; defaults to sha256 if not specified.

Note: Git LFS currently only supports the basic transfer adapter. This property was added for future compatibility with some experimental transfer adapters. See the API README for a list of the documented transfer adapters.

// POST https://lfs-server.com/objects/batch
// Accept: application/vnd.git-lfs+json
// Content-Type: application/vnd.git-lfs+json
// Authorization: Basic ... (if needed)
{
  "operation": "download",
  "transfers": [ "basic" ],
  "ref": { "name": "refs/heads/main" },
  "objects": [
    {
      "oid": "12345678",
      "size": 123
    }
  ],
  "hash_algo": "sha256"
}

Ref Property

The Batch API added the ref property in LFS v2.4 to support Git server authentication schemes that take the refspec into account. Since this is a new addition to the API, servers should be able to operate with a missing or null ref property.

Some examples will illustrate how the ref property can be used.

  • User owner has full access to the repository.
  • User contrib has readonly access to the repository, and write access to refs/heads/contrib.
{
  "operation": "download",
  "transfers": [ "basic" ],
  "objects": [
    {
      "oid": "12345678",
      "size": 123
    }
  ]
}

With this payload, both owner and contrib can download the requested object, since they both have read access.

{
  "operation": "upload",
  "transfers": [ "basic" ],
  "objects": [
    {
      "oid": "12345678",
      "size": 123
    }
  ]
}

With this payload, only owner can upload the requested object.

{
  "operation": "upload",
  "transfers": [ "basic" ],
  "ref": { "name": "refs/heads/contrib" },
  "objects": [
    {
      "oid": "12345678",
      "size": 123
    }
  ]
}

Both owner and contrib can upload the request object.

Successful Responses

The Batch API should always return with a 200 status, unless there are some issues with the request (bad authorization, bad json, etc). See below for examples of response errors. Check out the documented transfer adapters in the API README to see how Git LFS handles successful Batch responses.

Successful responses include the following properties:

  • transfer - String identifier of the transfer adapter that the server prefers. This MUST be one of the given transfer identifiers from the request. Servers can assume the basic transfer adapter if none were given. The Git LFS client will use the basic transfer adapter if the transfer property is omitted.
  • objects - An Array of objects to download.
    • oid - String OID of the LFS object.
    • size - Integer byte size of the LFS object. Must be at least zero.
    • authenticated - Optional boolean specifying whether the request for this specific object is authenticated. If omitted or false, Git LFS will attempt to find credentials for this URL.
    • actions - Object containing the next actions for this object. Applicable actions depend on which operation is specified in the request. How these properties are interpreted depends on which transfer adapter the client will be using.
      • href - String URL to download the object.
      • header - Optional hash of String HTTP header key/value pairs to apply to the request.
      • expires_in - Whole number of seconds after local client time when transfer will expire. Preferred over expires_at if both are provided. Maximum of 2147483647, minimum of -2147483647.
      • expires_at - String uppercase RFC 3339-formatted timestamp with second precision for when the given action expires (usually due to a temporary token).
  • hash_algo - The hash algorithm used to name Git LFS objects for this repository. Optional; defaults to sha256 if not specified.

Download operations MUST specify a download action, or an object error if the object cannot be downloaded for some reason. See “Response Errors” below.

Upload operations can specify an upload and a verify action. The upload action describes how to upload the object. If the object has a verify action, the LFS client will hit this URL after a successful upload. Servers can use this for extra verification, if needed. If a client requests to upload an object that the server already has, the server should omit the actions property completely. The client will then assume the server already has it.

// HTTP/1.1 200 Ok
// Content-Type: application/vnd.git-lfs+json
{
  "transfer": "basic",
  "objects": [
    {
      "oid": "1111111",
      "size": 123,
      "authenticated": true,
      "actions": {
        "download": {
          "href": "https://some-download.com",
          "header": {
            "Key": "value"
          },
          "expires_at": "2016-11-10T15:29:07Z"
        }
      }
    }
  ],
  "hash_algo": "sha256"
}

If there are problems accessing individual objects, servers should continue to return a 200 status code, and provide per-object errors. Here is an example:

// HTTP/1.1 200 Ok
// Content-Type: application/vnd.git-lfs+json
{
  "transfer": "basic",
  "objects": [
    {
      "oid": "1111111",
      "size": 123,
      "error": {
        "code": 404,
        "message": "Object does not exist"
      }
    }
  ],
  "hash_algo": "sha256"
}

LFS object error codes should match HTTP status codes where possible:

  • 404 - The object does not exist on the server.
  • 409 - The specified hash algorithm disagrees with the server’s acceptable options.
  • 410 - The object was removed by the owner.
  • 422 - Validation error.

Response Errors

LFS servers can respond with these other HTTP status codes:

  • 401 - The authentication credentials are needed, but were not sent. Git LFS will attempt to get the authentication for the request and retry immediately.
  • 403 - The user has read, but not write access. Only applicable when the operation in the request is “upload.”
  • 404 - The Repository does not exist for the user.
  • 422 - Validation error with one or more of the objects in the request. This means that none of the requested objects to upload are valid.

Error responses will not have an objects property. They will only have:

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 404 Not Found
// Content-Type: application/vnd.git-lfs+json

{
  "message": "Not found",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

HTTP 401 responses should include an LFS-Authenticate header to tell the client what form of authentication it requires. If omitted, Git LFS will assume Basic Authentication. This mirrors the standard WWW-Authenticate header with a custom header key so it does not trigger password prompts in browsers.

// HTTP/1.1 401 Unauthorized
// Content-Type: application/vnd.git-lfs+json
// LFS-Authenticate: Basic realm="Git LFS"

{
  "message": "Credentials needed",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

The following status codes can optionally be returned from the API, depending on the server implementation.

  • 406 - The Accept header needs to be application/vnd.git-lfs+json.
  • 413 - The batch API request contained too many objects or the request was otherwise too large.
  • 429 - The user has hit a rate limit with the server. Though the API does not specify any rate limits, implementors are encouraged to set some for availability reasons.
  • 501 - The server has not implemented the current method. Reserved for future use.
  • 507 - The server has insufficient storage capacity to complete the request.
  • 509 - The bandwidth limit for the user or repository has been exceeded. The API does not specify any bandwidth limit, but implementors may track usage.

Some server errors may trigger the client to retry requests, such as 500, 502, 503, and 504.

Basic Transfer API

The Basic transfer API is a simple, generic API for directly uploading and downloading LFS objects. Git LFS servers can offload object storage to cloud services like S3, or implement this API natively.

This is the original transfer adapter. All Git LFS clients and servers SHOULD support it, and default to it if the Batch API request or response do not specify a transfer property.

Downloads

Downloading an object requires a download action object in the Batch API response that looks like this:

{
  "transfer": "basic",
  "objects": [
    {
      "oid": "1111111",
      "size": 123,
      "authenticated": true,
      "actions": {
        "download": {
          "href": "https://some-download.com/1111111",
          "header": {
            "Authorization": "Basic ..."
          },
          "expires_in": 86400
        }
      }
    }
  ]
}

The Basic transfer adapter will make a GET request on the href, expecting the raw bytes returned in the HTTP response.

> GET https://some-download.com/1111111
> Authorization: Basic ...
<
< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
< Content-Length: 123
<
< {contents}

Uploads

The client uploads objects through individual PUT requests. The URL and headers are provided by an upload action object.

{
  "transfer": "basic",
  "objects": [
    {
      "oid": "1111111",
      "size": 123,
      "authenticated": true,
      "actions": {
        "upload": {
          "href": "https://some-upload.com/1111111",
          "header": {
            "Authorization": "Basic ..."
          },
          "expires_in": 86400
        }
      }
    }
  ]
}

The Basic transfer adapter will make a PUT request on the href, sending the raw bytes returned in the HTTP request.

> PUT https://some-upload.com/1111111
> Authorization: Basic ...
> Content-Type: application/octet-stream
> Content-Length: 123
>
> {contents}
>
< HTTP/1.1 200 OK

Verification

The Batch API can optionally return a verify action object in addition to an upload action object. If given, The Batch API expects a POST to the href after a successful upload.

{
  "transfer": "basic",
  "objects": [
    {
      "oid": "1111111",
      "size": 123,
      "authenticated": true,
      "actions": {
        "upload": {
          "href": "https://some-upload.com/1111111",
          "header": {
            "Authorization": "Basic ..."
          },
          "expires_in": 86400
        },
        "verify": {
          "href": "https://some-verify-callback.com",
          "header": {
            "Authorization": "Basic ..."
          },
          "expires_in": 86400
        }
      }
    }
  ]
}

Git LFS clients send:

  • oid - The String OID of the Git LFS object.
  • size - The integer size of the Git LFS object, in bytes.
> POST https://some-verify-callback.com
> Accept: application/vnd.git-lfs+json
> Content-Type: application/vnd.git-lfs+json
> Content-Length: 123
>
> {"oid": "{oid}", "size": 10000}
>
< HTTP/1.1 200 OK

The client may also include a charset=utf-8 parameter in the Content-Type header, which servers should be prepared to accept.

A 200 response means that the object exists on the server.

Git LFS File Locking API

Added: v2.0

The File Locking API is used to create, list, and delete locks, as well as verify that locks are respected in Git pushes. The locking URLs are built by adding a suffix to the LFS Server URL.

Git remote: https://git-server.com/foo/bar
LFS server: https://git-server.com/foo/bar.git/info/lfs
Locks API: https://git-server.com/foo/bar.git/info/lfs/locks

See the Server Discovery doc for more info on how LFS builds the LFS server URL.

All File Locking requests require the following HTTP headers:

Accept: application/vnd.git-lfs+json
Content-Type: application/vnd.git-lfs+json

The client may also include a charset=utf-8 parameter in the Content-Type header, which servers should be prepared to accept.

See the Authentication doc for more info on how LFS gets authorizes Batch API requests.

Note: This is the first version of the File Locking API, supporting only the simplest use case: single branch locking. The API is designed to be extensible as we experiment with more advanced locking scenarios, as defined in the original proposal.

The Batch API’s ref property docs describe how the ref property can be used to support auth schemes that include the server ref. Locking API implementations should also only use it for authentication, until advanced locking scenarios have been developed.

Create Lock

The client sends the following to create a lock by sending a POST to /locks (appended to the LFS server url, as described above). Servers should ensure that users have push access to the repository, and that files are locked exclusively to one user.

  • path - String path name of the file that is locked. This should be relative to the root of the repository working directory.
  • ref - Optional object describing the server ref that the locks belong to. Note: Added in v2.4.
    • name - Fully-qualified server refspec.
// POST https://lfs-server.com/locks
// Accept: application/vnd.git-lfs+json
// Content-Type: application/vnd.git-lfs+json
// Authorization: Basic ...
{
  "path": "foo/bar.zip",
  "ref": {
    "name": "refs/heads/my-feature"
  }
}

Successful Response

Successful responses return the created lock:

  • id - String ID of the Lock. Git LFS doesn’t enforce what type of ID is used, as long as it’s returned as a string.
  • path - String path name of the locked file. This should be relative to the root of the repository working directory.
  • locked_at - The timestamp the lock was created, as an uppercase RFC 3339-formatted string with second precision.
  • owner - Optional name of the user that created the Lock. This should be set from the user credentials posted when creating the lock.
// HTTP/1.1 201 Created
// Content-Type: application/vnd.git-lfs+json
{
  "lock": {
    "id": "some-uuid",
    "path": "foo/bar.zip",
    "locked_at": "2016-05-17T15:49:06+00:00",
    "owner": {
      "name": "Jane Doe"
    }
  }
}

Bad Response: Lock Exists

Lock services should reject lock creations if one already exists for the given path on the current repository.

  • lock - The existing Lock that clashes with the request.
  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 409 Conflict
// Content-Type: application/vnd.git-lfs+json
{
  "lock": {
    // details of existing lock
  },
  "message": "already created lock",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Unauthorized Response

Lock servers should require that users have push access to the repository before they can create locks.

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 403 Forbidden
// Content-Type: application/vnd.git-lfs+json
{
  "message": "You must have push access to create a lock",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Error Response

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 500 Internal server error
// Content-Type: application/vnd.git-lfs+json
{
  "message": "internal server error",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

List Locks

The client can request the current active locks for a repository by sending a GET to /locks (appended to the LFS server url, as described above). LFS Servers should ensure that users have at least pull access to the repository.

The properties are sent as URI query values, instead of through a JSON body:

  • path - Optional string path to match against locks on the server.
  • id - Optional string ID to match against a lock on the server.
  • cursor - The optional string value to continue listing locks. This value should be the next_cursor from a previous request.
  • limit - The integer limit of the number of locks to return. The server should have its own upper and lower bounds on the supported limits.
  • refspec - Optional fully qualified server refspec from which to search for locks.
// GET https://lfs-server.com/locks?path=&id=&cursor=&limit=&refspec=
// Accept: application/vnd.git-lfs+json
// Authorization: Basic ... (if needed)

Successful Response

A successful response will list the matching locks:

  • locks - Array of matching Lock objects. See the “Create Lock” successful response section to see what Lock properties are possible.
  • next_cursor - Optional string cursor that the server can return if there are more locks matching the given filters. The client will re-do the request, setting the ?cursor query value with this next_cursor value.

Note: If the server has no locks, it must return an empty locks array.

// HTTP/1.1 200 Ok
// Content-Type: application/vnd.git-lfs+json
{
  "locks": [
    {
      "id": "some-uuid",
      "path": "/path/to/file",
      "locked_at": "2016-05-17T15:49:06+00:00",
      "owner": {
        "name": "Jane Doe"
      }
    }
  ],
  "next_cursor": "optional next ID"
}

Unauthorized Response

Lock servers should require that users have pull access to the repository before they can list locks.

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 403 Forbidden
// Content-Type: application/vnd.git-lfs+json
{
  "message": "You must have pull access to list locks",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Error Response

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 500 Internal server error
// Content-Type: application/vnd.git-lfs+json
{
  "message": "unable to list locks",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

List Locks for Verification

The client can use the Lock Verification endpoint to check for active locks that can affect a Git push. For a caller, this endpoint is very similar to the “List Locks” endpoint above, except:

  • Verification requires a POST request.
  • The cursor, ref and limit values are sent as properties in the json request body.
  • The response includes locks partitioned into ours and theirs properties.

LFS Servers should ensure that users have push access to the repository.

Clients send the following to list locks for verification by sending a POST to /locks/verify (appended to the LFS server url, as described above):

  • ref - Optional object describing the server ref that the locks belong to. Note: Added in v2.4.
    • name - Fully-qualified server refspec.
  • cursor - Optional cursor to allow pagination. Servers can determine how cursors are formatted based on how they are stored internally.
  • limit - Optional limit to how many locks to return.
// POST https://lfs-server.com/locks/verify
// Accept: application/vnd.git-lfs+json
// Content-Type: application/vnd.git-lfs+json
// Authorization: Basic ...
{
  "cursor": "optional cursor",
  "limit": 100, // also optional
  "ref": {
    "name": "refs/heads/my-feature"
  }
}

Note: As more advanced locking workflows are implemented, more details will likely be added to this request body in future iterations.

Successful Response

A successful response will list the relevant locks:

  • ours - Array of Lock objects currently owned by the authenticated user. modify.
  • theirs - Array of Lock objects currently owned by other users.
  • next_cursor - Optional string cursor that the server can return if there are more locks matching the given filters. The client will re-do the request, setting the cursor property with this next_cursor value.

If a Git push updates any files matching any of “our” locks, Git LFS will list them in the push output, in case the user will want to unlock them after the push. However, any updated files matching one of “their” locks will halt the push. At this point, it is up to the user to resolve the lock conflict with their team.

Note: If the server has no locks, it must return an empty array in the ours or theirs properties.

// HTTP/1.1 200 Ok
// Content-Type: application/vnd.git-lfs+json
{
  "ours": [
    {
      "id": "some-uuid",
      "path": "/path/to/file",
      "locked_at": "2016-05-17T15:49:06+00:00",
      "owner": {
        "name": "Jane Doe"
      }
    }
  ],
  "theirs": [],
  "next_cursor": "optional next ID"
}

Not Found Response

By default, an LFS server that doesn’t implement any locking endpoints should return 404. This response will not halt any Git pushes.

Any 404 will do, but Git LFS will show a better error message with a json response.

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 404 Not found
// Content-Type: application/vnd.git-lfs+json
{
  "message": "Not found",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Unauthorized Response

Lock servers should require that users have push access to the repository before they can get a list of locks to verify a Git push.

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 403 Forbidden
// Content-Type: application/vnd.git-lfs+json
{
  "message": "You must have push access to verify locks",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Error Response

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 500 Internal server error
// Content-Type: application/vnd.git-lfs+json
{
  "message": "unable to list locks",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Delete Lock

The client can delete a lock, given its ID, by sending a POST to /locks/:id/unlock (appended to the LFS server url, as described above). LFS servers should ensure that callers have push access to the repository. They should also prevent a user from deleting another user’s lock, unless the force property is given.

Properties:

  • force - Optional boolean specifying that the user is deleting another user’s lock.
  • ref - Optional object describing the server ref that the locks belong to. Note: Added in v2.4.
    • name - Fully-qualified server refspec.
// POST https://lfs-server.com/locks/:id/unlock
// Accept: application/vnd.git-lfs+json
// Content-Type: application/vnd.git-lfs+json
// Authorization: Basic ...

{
  "force": true,
  "ref": {
    "name": "refs/heads/my-feature"
  }
}

Successful Response

Successful deletions return the deleted lock. See the “Create Lock” successful response section to see what Lock properties are possible.

// HTTP/1.1 200 Ok
// Content-Type: application/vnd.git-lfs+json
{
  "lock": {
    "id": "some-uuid",
    "path": "/path/to/file",
    "locked_at": "2016-05-17T15:49:06+00:00",
    "owner": {
      "name": "Jane Doe"
    }
  }
}

Unauthorized Response

Lock servers should require that users have push access to the repository before they can delete locks. Also, if the force parameter is omitted, or false, the user should only be allowed to delete locks that they created.

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 403 Forbidden
// Content-Type: application/vnd.git-lfs+json
{
  "message": "You must have push access to delete locks",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

Error response

  • message - String error message.
  • request_id - Optional String unique identifier for the request. Useful for debugging.
  • documentation_url - Optional String to give the user a place to report errors.
// HTTP/1.1 500 Internal server error
// Content-Type: application/vnd.git-lfs+json
{
  "message": "unable to delete lock",
  "documentation_url": "https://lfs-server.com/docs/errors",
  "request_id": "123"
}

git-lfs

Name

git-lfs — Git LFS — large file storage for git

Synopsis

git-lfs [COMMAND]

Description

Git LFS — large file storage for git

Git LFS is a system for managing and versioning large files in association with a Git repository. Instead of storing the large files within the Git repository as blobs, Git LFS stores special “pointer files” in the repository, while storing the actual file contents on a Git LFS server. The contents of the large file are downloaded automatically when needed, for example when a Git branch containing the large file is checked out.

Git LFS works by using a “smudge” filter to look up the large file contents based on the pointer file, and a “clean” filter to create a new version of the pointer file when the large file’s contents change. It also uses a pre-push hook to upload the large file contents to the Git LFS server whenever a commit containing a new large file version is about to be pushed to the corresponding Git server.

Options

Flags

  • -V, --version Print the version banner and exit

Subcommands

  • clean — Git clean filter that converts large files to pointers
  • smudge — Git smudge filter that converts pointer in blobs to the actual content
  • install — Install Git LFS configuration
  • uninstall — Remove Git LFS configuration
  • track — View or add Git LFS paths to Git attributes
  • untrack — Remove Git LFS paths from Git attributes
  • filter-process — Git filter process that converts between pointer and actual content
  • fetch — Download all Git LFS files for a given ref
  • pull — Download all Git LFS files for current ref and checkout
  • push — Push queued large files to the Git LFS endpoint
  • clone — Efficiently clone a LFS-enabled repository
  • post-checkout — Git post-checkout hook implementation
  • post-commit — Git post-commit hook implementation
  • post-merge — Git post-merge hook implementation
  • pre-push — Git pre-push hook implementation
  • version — Print the git-lfs version banner and exit
  • pointer — Build, compare, and check pointers
  • env — Display the Git LFS environment
  • ext — List the configured LFS pointer extensions
  • update — Update Git hooks
  • migrate — Migrate history to or from Git LFS
  • checkout — Populate working copy with real content from Git LFS files
  • prune — Delete old LFS files from local storage
  • fsck — Check Git LFS files for consistency
  • status — Show the status of Git LFS files in the working tree
  • lock — Set a file as “locked” on the Git LFS server
  • locks — Lists currently locked files from the Git LFS server
  • unlock — Remove “locked” setting for a file on the Git LFS server
  • ls-files — Show information about Git LFS files in the index and working tree
  • logs — Show errors logged by Git LFS
  • merge-driver — Merge driver for LFS-tracked files

Examples

To get started with Git LFS, the following commands can be used.

  1. Setup Git LFS on your system. You only have to do this once per user account:

    git lfs install
    
  2. Choose the type of files you want to track, for examples all ISO images, with git-lfs-track(1):

    git lfs track "*.iso"
    
  3. The above stores this information in gitattributes(5) files, so that file needs to be added to the repository:

    git add .gitattributes
    
  4. Commit, push and work with the files normally:

    git add file.iso
    git commit -m "Add disk image"
    git push
    

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-checkout

Name

git-lfs-checkout — Populate working copy with real content from Git LFS files

Synopsis

git-lfs-checkout [OPTIONS] [PATHS]...

Description

Populate working copy with real content from Git LFS files.

Try to ensure that the working copy contains file content for Git LFS objects for the current ref, if the object data is available. Does not download any content; see git-lfs-fetch(1) for that.

Checkout scans the current ref for all LFS objects that would be required, then where a file is either missing in the working copy, or contains placeholder pointer content with the same SHA, the real file content is written, provided we have it in the local store. Modified files are never overwritten.

One or more may be provided as arguments to restrict the set of files that are updated. Glob patterns are matched as per the format described in gitignore(5).

When used with --to and the working tree is in a conflicted state due to a merge, this option checks out one of the three stages a conflicting Git LFS object into a separate file (which can be outside of the work tree). This can make using diff tools to inspect and resolve merges easier. A single Git LFS object’s file path must be provided in PATHS. If FILE already exists, whether as a regular file, symbolic link, or directory, it will be removed and replaced, unless it is a non-empty directory or otherwise cannot be deleted.

If the installed Git version is at least 2.42.0, this command will by default check out Git LFS objects for files only if they are present in the Git index and if they match a Git LFS filter attribute from a .gitattributes file that is present in either the index or the current working tree (or, as is always the case, if they match a Git LFS filter attribute in a local gitattributes file such as $GIT_DIR/info/attributes). These constraints do not apply with prior versions of Git.

In a repository with a partial clone or sparse checkout, it is therefore advisable to check out all .gitattributes files from HEAD before using this command, if Git v2.42.0 or later is installed. Alternatively, the GIT_ATTR_SOURCE environment variable may be set to HEAD, which will cause Git to only read attributes from .gitattributes files in HEAD and ignore those in the index or working tree.

In a bare repository, this command prints an informational message and exits without modifying anything. In a future version, it may exit with an error.

Options

Arguments

  • <PATHS> Paths to check out.

    When empty, everything in HEAD’s tree is checked out. In conflict mode (--to <path> together with one of --base, --ours, or --theirs), exactly one path is required.

Flags

  • --base Check out the merge base of the specified file

  • --ours Check out our side (that of the current branch) of the conflict for the specified file

  • --theirs Check out their side (that of the other branch) of the conflict for the specified file

  • --to <FILE> If the working tree is in a conflicted state, check out the portion of the conflict specified by --base, --ours, or --theirs to the given path. Exactly one of these options is required

Examples

Checkout all files that are missing or placeholders:

git lfs checkout

Checkout a specific couple of files:

git lfs checkout path/to/file1.png path/to/file2.png

Checkout a path with a merge conflict into separate files:

# Attempt merge with a branch that has a merge conflict
$ git merge conflicting-branch
CONFLICT (content): Merge conflict in path/to/conflicting/file.dat

# Checkout versions of the conflicting file into temp files
$ git lfs checkout --to ours.dat --ours path/to/conflicting/file.dat
$ git lfs checkout --to theirs.dat --theirs path/to/conflicting/file.dat

# Compare conflicting versions in ours.dat and theirs.dat,
# then resolve conflict (e.g., by choosing one version over
# the other, or creating a new version)

# Cleanup and continue with merge
$ rm ours.dat theirs.dat
$ git add path/to/conflicting/file.dat
$ git merge --continue

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-clone

Name

git-lfs-clone — Efficiently clone a LFS-enabled repository

Synopsis

git-lfs-clone [ARGS]...

Description

Efficiently clone a LFS-enabled repository

Clone an LFS-enabled Git repository by disabling LFS during the git clone, then running git lfs pull directly afterwards. Also installs the repo-level hooks (.git/hooks) that LFS requires to operate; if --separate-git-dir is given to git clone, the hooks are installed there.

Historically faster than a regular git clone because that would download LFS content via the smudge filter one file at a time. Modern git clone parallelizes the smudge filter, so this command no longer offers a meaningful speedup over plain git clone. You should prefer plain git clone.

In addition to the options accepted by git clone, the LFS-only flags --include / -I <paths>, --exclude / -X <paths>, and --skip-repo (skip installing the repo-level hooks) are accepted — see git-lfs-fetch(1) for the include/exclude semantics. They’re parsed from the trailing argument list rather than declared as clap flags, so they don’t appear in this command’s --help.

Options

Arguments

  • <ARGS> git clone arguments plus the LFS pass-through flags (-I/--include, -X/--exclude, --skip-repo). The repository URL is required; an optional target directory follows

See also

git-clone(1), git-lfs-pull(1), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-env

Name

git-lfs-env — Display the Git LFS environment

Synopsis

git-lfs-env

Description

Display the Git LFS environment

Display the current Git LFS environment: version, endpoints, on-disk paths, and the three filter.lfs.* config values.

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-ext

Name

git-lfs-ext — List the configured LFS pointer extensions

Synopsis

git-lfs-ext [COMMAND]

Description

List the configured LFS pointer extensions

Print each lfs.extension.<name>.* entry resolved to its final configuration in priority order. Extensions chain external clean / smudge programs around each LFS object — see git-lfs-config(5) for how to configure them.

With no arguments, prints every configured extension. With list <name>..., prints only the named extensions (one block per name, in argument order).

Options

Subcommands

  • list — List configured LFS pointer extensions, optionally filtered by name

Examples

List details for all extensions:

git lfs ext

or equivalently:

git lfs ext list

List details for the specified extensions:

git lfs ext list foo bar

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-fetch

Name

git-lfs-fetch — Download all Git LFS files for a given ref

Synopsis

git-lfs-fetch [OPTIONS] [ARGS]...

Description

Download all Git LFS files for a given ref

Download Git LFS objects at the given refs from the specified remote. See DEFAULT REMOTE and DEFAULT REFS for what happens if you don’t specify.

This does not update the working copy; use git-lfs-pull(1) to download and replace pointer text with object content, or git-lfs-checkout(1) to materialize already-downloaded objects.

Options

Arguments

  • <ARGS> Optional remote name followed by refs. The first positional argument is treated as a remote name when it resolves; any following arguments are refs to fetch

Flags

  • -I, --include <INCLUDE> Specify lfs.fetchinclude just for this invocation; see INCLUDE AND EXCLUDE

  • -X, --exclude <EXCLUDE> Specify lfs.fetchexclude just for this invocation; see INCLUDE AND EXCLUDE

  • -a, --all Download all objects that are referenced by any commit reachable from the refs provided as arguments.

    If no refs are provided, then all refs are fetched. This is primarily for backup and migration purposes. Cannot be combined with --include/--exclude. Ignores any globally configured include and exclude paths to ensure that all objects are downloaded.

  • --stdin Read a list of newline-delimited refs from standard input instead of the command line

  • -p, --prune Prune old and unreferenced objects after fetching, equivalent to running git lfs prune afterwards. See git-lfs-prune(1) for more details

  • --refetch Also fetch objects that are already present locally.

    Useful for recovery from a corrupt local store.

  • -d, --dry-run Print what would be fetched, without actually fetching anything

  • -r, --recent Also fetch recently-touched refs and the recent pre-images on each.

    Walk every ref under refs/heads/ (and, by default, every remote-tracking ref) whose tip commit lies within lfs.fetchrecentrefsdays of today, and on each of those refs download the pre-image of every LFS file modified within lfs.fetchrecentcommitsdays. Combine with the named refs’ HEAD-state fetch. The same behaviour fires automatically if lfs.fetchrecentalways is set.

  • -j, --json Write the details of all object transfer requests as JSON to standard output.

    Intended for interoperation with external tools. When --dry-run is also specified, writes the details of the transfers that would occur if the objects were fetched.

Include and exclude

You can configure Git LFS to only fetch objects to satisfy references in certain paths of the repo, and/or to exclude certain paths of the repo, to reduce the time you spend downloading things you do not use.

In your Git configuration or in a .lfsconfig file, you may set either or both of lfs.fetchinclude and lfs.fetchexclude to comma-separated lists of paths. If lfs.fetchinclude is defined, Git LFS objects will only be fetched if their path matches one in that list, and if lfs.fetchexclude is defined, Git LFS objects will only be fetched if their path does not match one in that list. Paths are matched using wildcard matching as per gitignore(5).

Note that using the command-line options -I and -X override the respective configuration settings. Setting either option to an empty string clears the value.

Examples:

git config lfs.fetchinclude "textures,images/foo*"
This will only fetch objects referenced in paths in the textures folder, and files called foo* in the images folder.
git config lfs.fetchinclude "*.jpg,*.png,*.tga"
Only fetch JPG/PNG/TGA files, wherever they are in the repository.
git config lfs.fetchexclude "media/reallybigfiles"
Don’t fetch any LFS objects referenced in the folder media/reallybigfiles, but fetch everything else.
git config lfs.fetchinclude "media"
git config lfs.fetchexclude "media/excessive"
Only fetch LFS objects in the media folder, but exclude those in one of its subfolders.

Default remote

Without arguments, fetch downloads from the default remote. The default remote is the same as for git fetch, i.e. based on the remote branch you’re tracking first, or origin otherwise.

Default refs

If no refs are given as arguments, the currently checked out ref is used. With --recent (or lfs.fetchrecentalways=true), recently-touched refs and commits are also fetched — see RECENT CHANGES.

Recent changes

With --recent (or lfs.fetchrecentalways=true), fetch downloads objects from recently-active refs and commits in addition to the ones the named refs ask for. The idea is to pre-populate the cache so a later checkout or diff of “what we were working on last week” doesn’t trigger another download round-trip.

What counts as ‘recent’ is controlled by these gitconfig keys:

  • lfs.fetchrecentrefsdays: include branches whose tip commit is within this many days of now. Only local refs are scanned unless lfs.fetchrecentremoterefs is also set. Default 7.
  • lfs.fetchrecentremoterefs: also scan the remote-tracking refs of the remote being fetched. Useful for picking up branches you might check out later without first creating a tracking local ref. Default true.
  • lfs.fetchrecentcommitsdays: in addition to fetching the tip state of each recent ref, also fetch any LFS object referenced by commits within this many days of that ref’s tip. Default 0 (tip only).
  • lfs.fetchrecentalways: when true, always behave as if --recent was passed. Default false.

Examples

Fetch the LFS objects for the current ref from the default remote:

git lfs fetch

Fetch the LFS objects for the current ref AND recent changes from the default remote (see RECENT CHANGES):

git lfs fetch --recent

Fetch the LFS objects for the current ref from a secondary remote upstream:

git lfs fetch upstream

Fetch all the LFS objects from the default remote that are referenced by any commit in the main and develop branches:

git lfs fetch --all origin main develop

Fetch the LFS objects for a branch from origin:

git lfs fetch origin mybranch

Fetch the LFS objects for two branches and a commit from origin:

git lfs fetch origin main mybranch e445b45c1c9c6282614f201b62778e4c0688b5c8

See also

git-lfs-checkout(1), git-lfs-pull(1), git-lfs-prune(1), gitconfig(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-fsck

Name

git-lfs-fsck — Check Git LFS files for consistency

Synopsis

git-lfs-fsck [OPTIONS] [REFSPEC]

Description

Check Git LFS files for consistency

Check all Git LFS files in the current HEAD for consistency. Corrupted files are moved to .git/lfs/bad.

A single committish may be given to inspect that commit instead of HEAD. The <a>..<b> range form from upstream is not yet supported — only a single ref is accepted. With no argument, HEAD is examined.

The default is to perform all checks. lfs.fetchexclude is also not yet honored on this command; objects whose paths match the exclude list will still be checked.

Options

Arguments

  • <REFSPEC> Ref to scan. Defaults to HEAD

Flags

  • --objects Check that each object in HEAD matches its expected hash and that each object exists on disk

  • --pointers Check that each pointer is canonical and that each file which should be stored as a Git LFS file is so stored

  • -d, --dry-run Perform checks, but do not move any corrupted files to .git/lfs/bad

See also

git-lfs-ls-files(1), git-lfs-status(1), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-install

Name

git-lfs-install — Install Git LFS configuration

Synopsis

git-lfs-install [OPTIONS]

Description

Install Git LFS configuration

Set up the lfs smudge and clean filters under the name lfs in the global Git config, and (when run from inside a repository) install a pre-push hook to run git-lfs-pre-push(1). If core.hooksPath is configured in any Git configuration (supported on Git v2.9.0 or later), the pre-push hook is installed to that directory instead.

Without any options, only sets up the lfs smudge and clean filters if they are not already set.

Options

Flags

  • -f, --force Set the lfs smudge and clean filters, overwriting existing values

  • -l, --local Set the lfs smudge and clean filters in the local repository’s git config, instead of the global git config (~/.gitconfig)

  • -w, --worktree Set the lfs smudge and clean filters in the current working tree’s git config, instead of the global git config (~/.gitconfig) or local repository’s git config ($GIT_DIR/config).

    If multiple working trees are in use, the Git config extension worktreeConfig must be enabled to use this option. If only one working tree is in use, --worktree has the same effect as --local. Available only on Git v2.20.0 or later.

  • --system Set the lfs smudge and clean filters in the system git config, e.g. /etc/gitconfig instead of the global git config (~/.gitconfig)

  • --file <PATH> Set the lfs smudge and clean filters in the Git configuration file specified by <PATH>

  • -s, --skip-smudge Skip automatic downloading of objects on clone or pull.

    Requires a manual git lfs pull every time a new commit is checked out on the repository.

  • --skip-repo Skip installation of hooks into the local repository.

    Use if you want to install the LFS filters but not make changes to the hooks. Valid alongside --local, --worktree, --system, or --file.

See also

git-lfs-uninstall(1), git-worktree(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-lock

Name

git-lfs-lock — Set a file as “locked” on the Git LFS server

Synopsis

git-lfs-lock [OPTIONS] [PATHS]...

Description

Set a file as “locked” on the Git LFS server

Sets the given file path as “locked” against the Git LFS server, with the intention of blocking attempts by other users to update the given path. Locking a file requires the file to exist in the working copy.

Once locked, LFS will verify that Git pushes do not modify files locked by other users. See the description of the lfs.<url>.locksverify config key in git-lfs-config(5) for details.

Options

Arguments

  • <PATHS> Paths to lock. Repo-relative or absolute; must resolve inside the working tree. Upstream’s CLI accepts a single path; ours accepts multiple (additive extension)

Flags

  • -r, --remote <REMOTE> Specify the Git LFS server to use. Ignored if the lfs.url config key is set

  • -j, --json Write lock info as JSON to standard output if the command exits successfully.

    Intended for interoperation with external tools. If the command returns with a non-zero exit code, plain text messages are sent to standard error.

  • --ref <REFSPEC> Refspec to associate the lock with (extension over upstream).

    Defaults to the current branch’s tracked upstream (branch.<current>.merge) or the current branch’s full ref (refs/heads/<branch>).

See also

git-lfs-unlock(1), git-lfs-locks(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-locks

Name

git-lfs-locks — Lists currently locked files from the Git LFS server

Synopsis

git-lfs-locks [OPTIONS]

Description

Lists currently locked files from the Git LFS server

Lists current locks from the Git LFS server. Without filters, all locks visible to the configured remote are returned.

Options

Flags

  • -r, --remote <REMOTE> Specify the Git LFS server to use. Ignored if the lfs.url config key is set

  • -i, --id <ID> Specify a lock by its ID. Returns a single result

  • -p, --path <PATH> Specify a lock by its path. Returns a single result

  • --local List only our own locks which are cached locally. Skips a remote call.

    Useful when offline or to confirm what git lfs lock recorded locally. Combine with --path / --id / --limit to filter; --verify is rejected.

  • --verify Verify the lock owner on the server and mark our own locks with O.

    Own locks are held by us and the corresponding files can be updated for the next push. All other locks are held by someone else. Contrary to --local, this also detects locks held by us despite no local lock information being available (e.g. because the file had been locked from a different clone) and detects “broken” locks (e.g. someone else forcibly unlocked our files).

  • -l, --limit <LIMIT> Maximum number of results to return

  • -j, --json Write lock info as JSON to standard output if the command exits successfully.

    Intended for interoperation with external tools. If the command returns with a non-zero exit code, plain text messages are sent to standard error.

  • --ref <REFSPEC> Refspec to filter locks by (extension over upstream).

    Defaults to the current branch’s tracked upstream — same auto-resolution as git lfs lock.

See also

git-lfs-lock(1), git-lfs-unlock(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-ls-files

Name

git-lfs-ls-files — Show information about Git LFS files in the index and working tree

Synopsis

git-lfs-ls-files [OPTIONS] [REFSPEC]

Description

Show information about Git LFS files in the index and working tree

Display paths of Git LFS files that are found in the tree at the given reference. If no reference is given, scan the currently checked-out branch.

An asterisk (*) after the OID indicates a full object, a minus (-) indicates an LFS pointer.

Note: upstream’s --include / --exclude path filters aren’t yet supported. The two-references form (git lfs ls-files <a> <b>, to show files modified between two refs) is also not yet supported.

Options

Arguments

  • <REFSPEC> Ref to list. Defaults to HEAD

Flags

  • -l, --long Show the entire 64-character OID, instead of just the first 10

  • -s, --size Show the size of the LFS object in parentheses at the end of each line

  • -n, --name-only Show only the LFS-tracked file names

  • -a, --all Inspect the full history of the repository, not the current HEAD (or other provided reference).

    Includes previous versions of LFS objects that are no longer found in the current tree.

  • -d, --debug Show as much information as possible about an LFS file.

    Intended for manual inspection; the exact format may change at any time.

  • --deleted Include LFS pointers reachable from history but no longer present in the current tree

  • -j, --json Write Git LFS file information as JSON to standard output if the command exits successfully.

    Intended for interoperation with external tools. If --debug is also provided, that option takes precedence. If any of --long, --size, or --name-only are provided, those options will have no effect.

See also

git-lfs-status(1), git-lfs-config(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-migrate

Name

git-lfs-migrate — Migrate history to or from Git LFS

Synopsis

git-lfs-migrate <COMMAND>

Description

Migrate history to or from Git LFS

Convert files in a Git repository to or from Git LFS pointers, or summarize Git file sizes by file type. The import mode converts Git files (i.e. blobs) to Git LFS, the export mode does the reverse, and the info mode provides an informational summary useful for deciding which files to import or export.

In all modes, by default git lfs migrate operates only on the currently checked-out branch, and only on files added in commits which do not exist on any remote. Multiple options are available to override these defaults — see INCLUDE AND EXCLUDE REFERENCES.

When converting files to or from Git LFS, this command only changes your local repository and working copy, never any remotes. import and export are generally DESTRUCTIVE — they rewrite Git history, changing commits and generating new commit SHAs. (The exception is the --no-rewrite import sub-mode.) Always commit or stash any uncommitted work first, validate the result before pushing, and force-push the new history once you’re satisfied.

For info and import, all file types are considered by default. In import you’ll usually want filename patterns or --fixup; export requires at least one --include pattern. See INCLUDE AND EXCLUDE.

git lfs migrate will examine, create, and modify .gitattributes files as necessary. They are always assigned the default read/write permissions mode; symbolic links with that name halt the migration.

Options

Subcommands

  • import — Convert Git objects to Git LFS pointers
  • export — Convert Git LFS pointers to Git objects
  • info — Show information about repository size

Include and exclude

You can have git lfs migrate convert only files whose pathspec matches the --include glob patterns and does not match the --exclude glob patterns, either to reduce total migration time or to migrate part of your repo. Multiple patterns may be given using commas as delimiters.

Pattern matching is functionally equivalent to the .gitattributes format. In addition to simple file extension matches (e.g. *.gif), patterns may also specify directory paths, in which case the path/** form may be used to match recursively.

Note that this form of pattern matching for --include / --exclude is unique to git lfs migrate. Other commands which also take these options (such as git lfs ls-files) use the gitignore(5) form of pattern matching instead.

Include and exclude references

You can have git lfs migrate convert only files added in commits reachable from certain references — defined with --include-ref — and ignore files in commits reachable from references defined with --exclude-ref.

For example, given:

    D---E---F
   /         \
  A---B------C    refs/heads/my-feature
   \          \
    \          refs/heads/main
     \
      refs/remotes/origin/main

The commits reachable by each ref:

refs/heads/main:           C, B, A
refs/heads/my-feature:     F, E, D, B, A
refs/remotes/origin/main:  A

The following options would include commits F, E, D, C, and B but exclude commit A:

--include-ref=refs/heads/my-feature
--include-ref=refs/heads/main
--exclude-ref=refs/remotes/origin/main

The presence of --everything indicates that all commits reachable from all local and remote references should be migrated. Note that the remote refs themselves are never updated by the migration.

Examples

List the file types taking up the most space in your repository’s unpushed commits:

git lfs migrate info

Convert specific file types in unpushed commits to LFS:

git lfs migrate import --include="*.mp3,*.psd"

Check for large files and existing LFS objects across every branch:

git lfs migrate info --everything

Convert all zip files in every local branch to LFS:

git lfs migrate import --everything --include="*.zip"

Convert all files over 100K in every local branch:

git lfs migrate import --everything --above=100Kb

Migrate to Git LFS in a single new commit (no history rewrite):

git lfs track "*.zip" "*.mp3" "*.psd"
git add .gitattributes
git commit -m "add Git LFS attributes"
git lfs migrate import --no-rewrite --yes test.zip audios/*.mp3 images/*.psd

Convert all zip Git LFS objects back to regular Git blobs:

git lfs migrate export --include-ref=main --include="*.zip"

After any history-rewriting migration, force-push the rewritten branches to your remotes — this alters Git history on your remotes and should be done with care.

See also

git-lfs-checkout(1), git-lfs-ls-files(1), git-lfs-track(1), git-lfs-untrack(1), gitattributes(5), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-migrate-import

Name

git-lfs-migrate-import — Convert Git objects to Git LFS pointers

Synopsis

git-lfs-migrate-import [OPTIONS] [ARGS]...

Description

Convert Git objects to Git LFS pointers

Migrate objects present in the Git history to pointer files tracked and stored with Git LFS. Adds entries for the converted file types to .gitattributes, creating those files if they don’t exist — as if git lfs track had been run at the points in history where each type first appears.

With --fixup, examine existing .gitattributes files and convert only Git objects that should be tracked by Git LFS according to those rules but aren’t yet.

With --no-rewrite, migrate objects to pointers in a single new commit on top of HEAD without rewriting history. The base migrate options (--include-ref, --everything, etc.) are ignored in this sub-mode, and the positional argument list changes from branches to a list of files. Files must be tracked by patterns already in .gitattributes.

Options

Arguments

  • <ARGS> Branches to rewrite (default: the currently checked-out branch). With --no-rewrite, instead a list of working-tree files to convert. References prefixed with ^ are excluded

Flags

  • -I, --include <INCLUDE> Convert paths matching this glob (repeatable, comma-delimited). Required unless --above is set or --no-rewrite is given

  • -X, --exclude <EXCLUDE> Exclude paths matching this glob (repeatable, comma-delimited)

  • --include-ref <INCLUDE_REF> Restrict the rewrite to commits reachable from these refs. Repeatable

  • --exclude-ref <EXCLUDE_REF> Exclude commits reachable from these refs. Repeatable

  • --everything Consider all commits reachable from any local or remote ref.

    Only local refs are updated even with --everything; remote refs stay synchronized with their remote.

  • --above <ABOVE> Only migrate files whose individual filesize is above the given size (e.g. 1b, 20 MB, 3 TiB).

    Cannot be used with --include, --exclude, or --fixup.

  • --no-rewrite Migrate objects in a new commit on top of HEAD without rewriting Git history.

    Switches to a different argument list (positional args become files, not branches) and ignores the core migrate options (--include-ref, --everything, etc.).

  • -m, --message <MESSAGE> Commit message for the --no-rewrite commit.

    If omitted, a message is generated from the file arguments.

  • --fixup Infer --include and --exclude filters per-commit from the repository’s .gitattributes files.

    Imports filepaths that should be tracked by Git LFS but aren’t yet pointers. Incompatible with explicitly given --include / --exclude filters.

  • --object-map <OBJECT_MAP> Write a CSV of <OLD-SHA>,<NEW-SHA> for every rewritten commit to the named file

  • --verbose Print the commit OID and filename of migrated files to standard output

  • --remote <REMOTE> Remote to consult when fetching missing LFS objects (default origin)

  • --skip-fetch Don’t refresh the known set of remote references before determining the set of “un-pushed” commits to migrate.

    Has no effect when combined with --include-ref or --exclude-ref.

  • --yes Assume a yes answer to any prompts, permitting noninteractive use.

    Currently we don’t prompt for any reason, so this is accepted as a no-op for upstream parity.

Examples

Convert specific file types in unpushed commits to LFS:

git lfs migrate import --include="*.mp3,*.psd"

Convert all zip files across every local branch:

git lfs migrate import --everything --include="*.zip"

Convert every file over 100K in every local branch:

git lfs migrate import --everything --above=100Kb

Repair already-committed files that should be LFS pointers according to .gitattributes but aren’t (e.g. committed while git lfs install wasn’t active):

git lfs migrate import --fixup

Migrate to Git LFS in a single new commit on top of HEAD without rewriting history:

git lfs track "*.zip" "*.mp3" "*.psd"
git add .gitattributes
git commit -m "add Git LFS attributes"
git lfs migrate import --no-rewrite test.zip audios/*.mp3 images/*.psd

After any history-rewriting migration, force-push the rewritten branches — this alters Git history on your remotes and should be done with care.

See also

git-lfs-migrate(1), git-lfs-migrate-export(1), git-lfs-migrate-info(1), git-lfs-track(1), gitattributes(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-migrate-export

Name

git-lfs-migrate-export — Convert Git LFS pointers to Git objects

Synopsis

git-lfs-migrate-export [OPTIONS] [BRANCHES]...

Description

Convert Git LFS pointers to Git objects

Migrate Git LFS pointer files present in the Git history out of Git LFS, converting them back into their corresponding object files. Files matching the --include patterns are removed from Git LFS; files matching --exclude retain their LFS status. Modifies .gitattributes to set/unset the relevant filepath patterns.

At least one --include pattern is required. Objects not present in the local LFS store are downloaded from the --remote (defaults to origin). Pointers whose objects can’t be fetched are left as-is.

Options

Arguments

  • <BRANCHES> Branches to rewrite (default: the currently checked-out branch). References prefixed with ^ are excluded

Flags

  • -I, --include <INCLUDE> Convert pointers at paths matching this glob (repeatable, comma-delimited). Required — at least one must be given

  • -X, --exclude <EXCLUDE> Don’t convert pointers at paths matching this glob (repeatable, comma-delimited)

  • --include-ref <INCLUDE_REF> Restrict the rewrite to commits reachable from these refs. Repeatable

  • --exclude-ref <EXCLUDE_REF> Exclude commits reachable from these refs. Repeatable

  • --everything Consider all commits reachable from any local or remote ref.

    Only local refs are updated even with --everything; remote refs stay synchronized with their remote.

  • --object-map <OBJECT_MAP> Write a CSV of <OLD-SHA>,<NEW-SHA> for every rewritten commit to the named file.

    Useful as input to git filter-repo or other downstream tools.

  • --verbose Print the commit OID and filename of migrated files to standard output

  • --remote <REMOTE> Download LFS objects from this remote during the export. Defaults to origin

  • --skip-fetch Don’t refresh the known set of remote references before the rewrite

  • --yes Assume a yes answer to any prompts, permitting noninteractive use.

    Currently we don’t prompt for any reason, so this is accepted as a no-op for upstream parity.

Examples

Convert all zip Git LFS pointers on main back to regular Git blobs:

git lfs migrate export --include-ref=main --include="*.zip"

Pointers whose objects aren’t in the local store are downloaded from the --remote (defaults to origin); pointers that can’t be downloaded are left as-is.

After exporting, the rewritten branches need to be force-pushed — this rewrites history on the remote.

See also

git-lfs-migrate(1), git-lfs-migrate-import(1), git-lfs-migrate-info(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-migrate-info

Name

git-lfs-migrate-info — Show information about repository size

Synopsis

git-lfs-migrate-info [OPTIONS] [BRANCHES]...

Description

Show information about repository size

Summarize the sizes of file objects present in the Git history, grouped by filename extension. Read-only — no objects or history change.

Existing Git LFS pointers are followed by default (the size of the referenced objects is totaled in a separate “LFS Objects” line). Use --pointers=ignore to skip pointers entirely, or --pointers=no-follow to count the pointer-text size as if the pointers were regular files (the older Git LFS behavior).

Options

Arguments

  • <BRANCHES> Branches to scan (default: the currently checked-out branch). References prefixed with ^ are excluded

Flags

  • -I, --include <INCLUDE> Only include paths matching this glob (repeatable, comma-delimited)

  • -X, --exclude <EXCLUDE> Exclude paths matching this glob (repeatable, comma-delimited)

  • --include-ref <INCLUDE_REF> Restrict the scan to commits reachable from these refs. Repeatable

  • --exclude-ref <EXCLUDE_REF> Exclude commits reachable from these refs. Repeatable

  • --everything Consider all commits reachable from any local or remote ref

  • --above <ABOVE> Only count files whose individual filesize is above the given size (e.g. 1b, 20 MB, 3 TiB).

    File-extension groups whose largest file is below --above don’t appear in the output.

  • --top <TOP> Display the top N entries, ordered by total file count.

    Default 5. When existing Git LFS objects are found, an extra “LFS Objects” line is output in addition to the top N entries (unless --pointers changes this).

  • --pointers <POINTERS> How to handle existing LFS pointer blobs.

    follow (default): summarize referenced objects in a separate “LFS Objects” line. ignore: skip pointers entirely. no-follow: count pointer-text size as if pointers were regular files (the older Git LFS behavior). When --fixup is given, defaults to ignore.

  • --unit <UNIT> Format byte quantities in this unit.

    Valid units: b, kib, mib, gib, tib, pib (IEC) or b, kb, mb, gb, tb, pb (SI). Auto-scaled when omitted.

  • --fixup Infer --include and --exclude filters per-commit from the repository’s .gitattributes files.

    Counts filepaths that should be tracked by Git LFS but aren’t yet pointers. Incompatible with explicit --include / --exclude filters and with --pointers settings other than ignore. Implies --pointers=ignore if not set.

  • --skip-fetch Don’t refresh the known set of remote references before the scan

  • --remote <REMOTE> Remote to consult (currently a no-op; reserved for the auto-fetch path)

Examples

List the file types taking up the most space in unpushed commits:

git lfs migrate info

Check large files and existing LFS objects across every branch (local + remote):

git lfs migrate info --everything

Report files that should be tracked by Git LFS according to the repository’s .gitattributes but aren’t yet pointers — the candidate set for git lfs migrate import --fixup:

git lfs migrate info --fixup

See also

git-lfs-migrate(1), git-lfs-migrate-import(1), git-lfs-migrate-export(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-prune

Name

git-lfs-prune — Delete old LFS files from local storage

Synopsis

git-lfs-prune [OPTIONS]

Description

Deletes local copies of LFS files that have aged out and are no longer needed, freeing disk space. Prune walks the local object store and removes anything not retained by at least one of:

  • HEAD’s tree in the current checkout
  • HEAD’s tree in any linked worktree (git worktree)
  • The stash
  • A ‘recent ref’ — see RECENT FILES
  • A ‘recent commit’ on HEAD or any recent ref — see RECENT FILES
  • An unpushed commit — see UNPUSHED LFS FILES

In short: prune deletes objects you aren’t currently using and that aren’t ‘recent’, as long as they’ve been pushed. The reflog isn’t consulted, so LFS objects only reachable from orphaned commits are always deleted.

lfs.fetchexclude / lfs.fetchinclude (comma-separated gitignore-style patterns) restrict which paths each retention producer scans. See git-lfs-config(5).

Note: don’t run git lfs prune when multiple repositories share a custom storage directory. See lfs.storage in git-lfs-config(5) for the implications.

Options

Flags

  • -d, --dry-run Don’t actually delete anything; just report what would have been done

  • -v, --verbose Report the full detail of what is/would be deleted

  • --recent Ignore the recent-refs / recent-commits retention windows when computing what is prunable. Equivalent to setting lfs.fetchrecentrefsdays and lfs.fetchrecentcommitsdays to 0 for this invocation

  • -f, --force Treat every pushed object as prunable regardless of the recent-refs / recent-commits / unpushed retention rules. Pointers reachable from HEAD’s tree are still kept

  • -c, --verify-remote Verify with the remote that prunable objects exist there before deleting them locally. With this on, an object that can’t be served by the remote either halts the prune (default) or is dropped from the delete set (--when-unverified=continue). Reachable-but-unverified objects are reported as missing on remote:; unreachable objects (orphans not in any commit) are silently passed through unless --verify-unreachable is also set. Overrides lfs.pruneverifyremotealways

  • --no-verify-remote Override lfs.pruneverifyremotealways=true and skip the remote verify pass for this invocation

  • --verify-unreachable When --verify-remote is in effect, verify orphan objects (not reachable from any commit) too. Without this, orphans pass through verification silently and are still pruned. Overrides lfs.pruneverifyunreachablealways

  • --no-verify-unreachable Override lfs.pruneverifyunreachablealways=true and skip orphan verification for this invocation

  • --when-unverified <MODE> What to do when --verify-remote finds objects missing on the remote. halt (the default) refuses the prune and lists the missing OIDs; continue drops them from the delete set and prunes the verified ones

Recent files

Prune keeps LFS files referenced by ‘recent’ commits so you can switch back to them without re-downloading. ‘Recent’ has the same meaning here as for git lfs fetch --recent, with an extra offset (default 3 days) so files you downloaded recently stick around for a while.

Settings:

  • lfs.pruneoffsetdays: extra days added to the fetch-recent windows. A ref or commit has to be at least this many days older than the oldest one --recent would download for prune to consider it old enough to delete. Default 3. Only takes effect when the underlying lfs.fetchrecent*days setting is non-zero.
  • lfs.fetchrecentrefsdays, lfs.fetchrecentremoterefs, lfs.fetchrecentcommitsdays: same meaning as in git-lfs-fetch(1), used as the base for the offset above. A day value of 0 disables that retention dimension entirely (everything outside the other rules becomes prunable).

Unpushed lfs files

LFS files reachable from a commit that hasn’t reached the remote are never pruned, regardless of age — the local copy is the only one.

‘Pushed’ is determined by comparing local refs against the remote’s refs: any LFS file referenced by a commit reachable from a local ref but not from the corresponding remote ref is treated as unpushed. The pre-push hook uploads LFS objects before the remote branch updates, so this comparison gives an accurate picture.

See DEFAULT REMOTE for which remote anchors the comparison.

Verify remote

--verify-remote (-c) asks the remote whether every prunable LFS file has a server-side copy before deleting it locally. The UNPUSHED LFS FILES check above is usually enough, but --verify-remote adds belt-and-braces for cases where you want to be sure (at the cost of extra batch calls to the server).

Enable as the default by setting lfs.pruneverifyremotealways=true.

--verify-unreachable extends the verification pass to LFS objects that aren’t referenced by any commit (orphans — added to the index but never committed, or referenced only by orphaned commits). Without this flag, orphans pass through --verify-remote silently and are deleted. Enable as the default with lfs.pruneverifyunreachablealways=true.

By default, --verify-remote halts the entire prune if any object can’t be verified. Pass --when-unverified=continue to instead drop the unverifiable objects from the delete set and proceed with the rest.

See DEFAULT REMOTE for which remote is queried.

Default remote

origin is the default remote consulted for UNPUSHED LFS FILES and VERIFY REMOTE. Even with multiple remotes configured, prune treats this one as canonical — usually it’s the main central repo (or your fork of it), and a valid backup of your work.

If origin isn’t configured, prune treats every reachable LFS file as unpushed and effectively retains everything.

Override the canonical remote with lfs.pruneremotetocheck: set it to a different remote name to anchor against that one instead.

See also

git-lfs-fetch(1), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-pull

Name

git-lfs-pull — Download all Git LFS files for current ref and checkout

Synopsis

git-lfs-pull [OPTIONS] [ARGS]...

Description

Download all Git LFS files for current ref and checkout

Download Git LFS objects for the currently checked out ref, and update the working copy with the downloaded content if required.

This is generally equivalent to running git lfs fetch [options] [<remote>] followed by git lfs checkout. See git-lfs-checkout(1) for partial-clone, sparse-checkout, and bare-repository behavior (governed by the installed Git version and GIT_ATTR_SOURCE).

Requires git lfs install to have wired up the smudge filter. If the filter is missing, the fetch step still runs but the working-tree update is skipped with a hint to install.

Options

Arguments

  • <ARGS> Optional remote name followed by refs.

    The first positional argument is treated as a remote name when it resolves; any following arguments are refs to fetch. With no arguments, the default remote is used.

Flags

  • -I, --include <INCLUDE> Specify lfs.fetchinclude just for this invocation

  • -X, --exclude <EXCLUDE> Specify lfs.fetchexclude just for this invocation

Include and exclude

You can configure Git LFS to only fetch objects to satisfy references in certain paths of the repo, and/or to exclude certain paths of the repo, to reduce the time you spend downloading things you do not use.

In your Git configuration or in a .lfsconfig file, you may set either or both of lfs.fetchinclude and lfs.fetchexclude to comma-separated lists of paths. If lfs.fetchinclude is defined, Git LFS objects will only be fetched if their path matches one in that list, and if lfs.fetchexclude is defined, Git LFS objects will only be fetched if their path does not match one in that list. Paths are matched using wildcard matching as per gitignore(5).

Note that using the command-line options -I and -X override the respective configuration settings. Setting either option to an empty string clears the value.

Default remote

Without arguments, pull downloads from the default remote. The default remote is the same as for git pull, i.e. based on the remote branch you’re tracking first, or origin otherwise.

Examples

Download LFS objects for the current ref from the default remote, then update the working tree:

git lfs pull

Pull from a specific remote:

git lfs pull upstream

Pull, but only fetch LFS objects whose paths match a glob (overrides lfs.fetchinclude for this invocation):

git lfs pull -I "textures/**,*.psd"

Pull and skip a path subtree (overrides lfs.fetchexclude):

git lfs pull -X "media/reallybigfiles"

See also

git-lfs-fetch(1), git-lfs-checkout(1), gitattributes(5), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-push

Name

git-lfs-push — Push queued large files to the Git LFS endpoint

Synopsis

git-lfs-push [OPTIONS] <REMOTE> [ARGS]...

Description

Push queued large files to the Git LFS endpoint

Upload Git LFS files to the configured endpoint for the current Git remote. By default, filters out objects that are already referenced by the local clone of the remote (approximated via refs/remotes/<remote>/*); the server’s batch API dedupes again, so a missing local tracking ref doesn’t waste bandwidth.

Options

Arguments

  • <REMOTE> Remote to push to (e.g. origin). The remote’s tracking refs are excluded from the upload set so already-pushed objects aren’t sent again

  • <ARGS> Refs (or, with --object-id, raw OIDs) to push. With --all, restricts the all-refs walk to these; with --stdin, ignored (a warning is emitted)

Flags

  • -d, --dry-run Print the files that would be pushed, without actually pushing them

  • -a, --all Push all objects reachable from the refs given as arguments.

    If no refs are provided, all local refs are pushed. Note this behavior differs from git lfs fetch --all, which fetches every ref including refs outside refs/heads / refs/tags. If you’re migrating a repository, run git lfs push for any additional remote refs that contain LFS objects not reachable from your local refs.

  • -o, --object-id Push only the object OIDs listed on the command line (or read from stdin with --stdin), separated by spaces

  • --stdin Read newline-delimited refs (or object IDs when using --object-id) from standard input instead of the command line

See also

git-lfs-fetch(1), git-lfs-pre-push(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-status

Name

git-lfs-status — Show the status of Git LFS files in the working tree

Synopsis

git-lfs-status [OPTIONS]

Description

Show the status of Git LFS files in the working tree

Display paths of Git LFS objects that have not been pushed to the Git LFS server (large files that would be uploaded by git push), that have differences between the index file and the current HEAD commit (large files that would be committed by git commit), or that have differences between the working tree and the index file (files that could be staged with git add).

Must be run in a non-bare repository.

Options

Flags

  • -p, --porcelain Give the output in an easy-to-parse format for scripts

  • -j, --json Write Git LFS file status information as JSON to standard output if the command exits successfully.

    Intended for interoperation with external tools. If --porcelain is also provided, that option takes precedence.

See also

git-lfs-ls-files(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-track

Name

git-lfs-track — View or add Git LFS paths to Git attributes

Synopsis

git-lfs-track [OPTIONS] [PATTERNS]...

Description

View or add Git LFS paths to Git attributes

Start tracking the given pattern(s) through Git LFS. The argument is written to .gitattributes. If no paths are provided, list the currently-tracked paths.

Per gitattributes(5), patterns use the gitignore(5) pattern rules to match paths. This means that patterns containing asterisk (*), question mark (?), and the bracket characters ([ and ]) are treated specially; to disable this behavior and treat them literally instead, use --filename or escape the character with a backslash.

Options

Arguments

  • <PATTERNS> File patterns to track (e.g. *.jpg, data/*.bin)

Flags

  • -v, --verbose Log files which git lfs track will touch. Disabled by default

  • -d, --dry-run Log all actions that would normally take place (adding entries to .gitattributes, touching files on disk, etc.) without performing any mutative operations.

    Implicitly mocks the behavior of --verbose, logging in greater detail what it is doing. Disabled by default.

  • -j, --json Write the currently tracked patterns as JSON to standard output.

    Intended for interoperation with external tools. Cannot be combined with any pattern arguments. If --no-excluded is also provided, that option will have no effect.

  • --filename Treat the arguments as literal filenames, not as patterns.

    Any special glob characters in the filename will be escaped when writing the .gitattributes file.

  • -l, --lockable Make the paths “lockable” — they should be locked to edit them, and will be made read-only in the working copy when not locked

  • --not-lockable Remove the lockable flag from the paths so they are no longer read-only unless locked

  • --no-excluded Don’t list patterns that are excluded in the output; only list patterns that are tracked

  • --no-modify-attrs Make matched entries stat-dirty so that Git can re-index files you wish to convert to LFS.

    Does not modify any .gitattributes file.

Examples

List the patterns that Git LFS is currently tracking:

git lfs track

Configure Git LFS to track GIF files:

git lfs track "*.gif"

Configure Git LFS to track PSD files and make them read-only unless locked:

git lfs track --lockable "*.psd"

Configure Git LFS to track the file named project [1].psd:

git lfs track --filename "project [1].psd"

See also

git-lfs-untrack(1), git-lfs-install(1), gitattributes(5), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-uninstall

Name

git-lfs-uninstall — Remove Git LFS configuration

Synopsis

git-lfs-uninstall [OPTIONS] [MODE]

Description

Remove Git LFS configuration

Remove the lfs clean and smudge filters from the global Git config, and (when run from inside a Git repository) uninstall the Git LFS pre-push hook. Hooks that don’t match what we would write are left untouched.

Options

Arguments

  • <MODE> Optional mode. With hooks, removes only the LFS git hooks and leaves the filter config alone (the inverse of --skip-repo)

Flags

  • -l, --local Remove the lfs smudge and clean filters from the local repository’s git config, instead of the global git config (~/.gitconfig)

  • -w, --worktree Remove the lfs smudge and clean filters from the current working tree’s git config, instead of the global git config (~/.gitconfig) or local repository’s git config ($GIT_DIR/config).

    If multiple working trees are in use, the Git config extension worktreeConfig must be enabled to use this option. If only one working tree is in use, --worktree has the same effect as --local. Available only on Git v2.20.0 or later.

  • --system Remove the lfs smudge and clean filters from the system git config, instead of the global git config (~/.gitconfig)

  • --file <PATH> Remove the lfs smudge and clean filters from the Git configuration file specified by <PATH>

  • --skip-repo Skip cleanup of the local repo.

    Use if you want to uninstall the global LFS filters but not make changes to the current repo.

See also

git-lfs-install(1), git-worktree(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-unlock

Name

git-lfs-unlock — Remove “locked” setting for a file on the Git LFS server

Synopsis

git-lfs-unlock [OPTIONS] [PATHS]...

Description

Remove “locked” setting for a file on the Git LFS server

Removes the given file path as “locked” on the Git LFS server. Files must exist and have a clean git status before they can be unlocked. The --force flag will skip these checks.

Options

Arguments

  • <PATHS> Paths to unlock. Upstream’s CLI accepts a single path; ours accepts multiple (additive extension). Mutually exclusive with --id

Flags

  • -r, --remote <REMOTE> Specify the Git LFS server to use. Ignored if the lfs.url config key is set

  • -f, --force Tell the server to remove the lock, even if it’s owned by another user

  • -i, --id <ID> Specify a lock by its ID instead of path. Mutually exclusive with the positional paths

  • -j, --json Write lock info as JSON to standard output if the command exits successfully.

    Intended for interoperation with external tools. If the command returns with a non-zero exit code, plain text messages are sent to standard error.

  • --ref <REFSPEC> Refspec to send with the unlock request (extension over upstream).

    Defaults to the current branch’s tracked upstream — same auto-resolution as git lfs lock.

See also

git-lfs-lock(1), git-lfs-locks(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-untrack

Name

git-lfs-untrack — Remove Git LFS paths from Git attributes

Synopsis

git-lfs-untrack [PATTERNS]...

Description

Remove Git LFS paths from Git attributes

Stop tracking the given path(s) through Git LFS. The argument can be a glob pattern or a file path. The matching pointer files in history (and the objects in the local store) are left in place.

Options

Arguments

  • <PATTERNS> Paths or glob patterns to stop tracking

Examples

Configure Git LFS to stop tracking GIF files:

git lfs untrack "*.gif"

See also

git-lfs-track(1), git-lfs-install(1), gitattributes(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-version

Name

git-lfs-version — Print the git-lfs version banner and exit

Synopsis

git-lfs-version

Description

Print the git-lfs version banner and exit

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-clean

Name

git-lfs-clean — Git clean filter that converts large files to pointers

Synopsis

git-lfs-clean [PATH]

Description

Git clean filter that converts large files to pointers

Read the contents of a large file from standard input, and write a Git LFS pointer file for that file to standard output.

Clean is typically run by Git’s clean filter, configured by the repository’s Git attributes.

Clean is not part of the user-facing Git plumbing commands. To preview the pointer of a large file as it would be generated, see the git-lfs-pointer(1) command.

Options

Arguments

  • <PATH> Working-tree path of the file being cleaned.

    Substituted for %f in any configured lfs.extension.<name>.clean command.

See also

git-lfs-install(1), git-lfs-push(1), git-lfs-pointer(1), gitattributes(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-filter-process

Name

git-lfs-filter-process — Git filter process that converts between pointer and actual content

Synopsis

git-lfs-filter-process [OPTIONS]

Description

Git filter process that converts between pointer and actual content

Implement the Git process filter API, exchanging handshake messages and then accepting and responding to requests to either clean or smudge a file.

filter-process is always run by Git’s filter process, and is configured by the repository’s Git attributes.

In your Git configuration or in a .lfsconfig file, you may set either or both of lfs.fetchinclude and lfs.fetchexclude to comma-separated lists of paths. If lfs.fetchinclude is defined, Git LFS pointer files will only be replaced with the contents of the corresponding object file if their path matches one in that list, and if lfs.fetchexclude is defined, pointer files will only be replaced if their path does not match one in that list. Paths are matched using wildcard matching as per gitignore(5). Pointer files that are not replaced are simply copied to standard output without change.

The filter process uses Git’s pkt-line protocol to communicate, and is documented in detail in gitattributes(5).

Options

Flags

  • -s, --skip Skip automatic downloading of objects on clone or pull.

    Equivalent to GIT_LFS_SKIP_SMUDGE=1. Wired up by git lfs install --skip-smudge.

See also

git-lfs-clean(1), git-lfs-install(1), git-lfs-smudge(1), gitattributes(5), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-pointer

Name

git-lfs-pointer — Build, compare, and check pointers

Synopsis

git-lfs-pointer [OPTIONS]

Description

Build, compare, and check pointers

Build and optionally compare generated pointer files to ensure consistency between different Git LFS implementations.

Options

Flags

  • -f, --file <FILE> A local file to build the pointer from

  • -p, --pointer <POINTER> A local file containing a pointer generated from another implementation.

    Compared to the pointer generated from --file.

  • --stdin Read the pointer from standard input to compare with the pointer generated from --file

  • --check Read the pointer from standard input (with --stdin) or the filepath (with --file).

    If neither or both of --stdin and --file are given, the invocation is invalid. Exits 0 if the data read is a valid Git LFS pointer, 1 otherwise. With --strict, exits 2 if the pointer is not byte-canonical.

  • --strict With --check, verify that the pointer is canonical (the one Git LFS would create).

    If it isn’t, exits 2. The default — for backwards compatibility — is --no-strict.

  • --no-strict Disable strict mode (paired with --strict)

  • --no-extensions Build a plain pointer without running configured lfs.extension.* clean commands. Default behavior is to chain through any extensions (and emit a warning: line on stderr); pass this to suppress both the chain and the warning

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-pre-push

Name

git-lfs-pre-push — Git pre-push hook implementation

Synopsis

git-lfs-pre-push [OPTIONS] <REMOTE> [URL]

Description

Git pre-push hook implementation

Respond to Git pre-push events. Reads the range of commits from stdin in the form <local-ref> <local-sha1> <remote-ref> <remote-sha1>, takes the remote name and URL as arguments, and uploads any Git LFS objects associated with those commits to the Git LFS API.

When pushing a new branch, the list of Git objects considered is every object reachable from the new branch. When deleting a branch, no LFS objects are pushed.

Options

Arguments

  • <REMOTE> Name of the remote being pushed to

  • <URL> URL of the remote (informational; we use the lfs.url config)

Flags

  • -d, --dry-run Print the files that would be pushed, without actually pushing them

See also

git-lfs-clean(1), git-lfs-push(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-smudge

Name

git-lfs-smudge — Git smudge filter that converts pointer in blobs to the actual content

Synopsis

git-lfs-smudge [OPTIONS] [PATH]

Description

Git smudge filter that converts pointer in blobs to the actual content

Read a Git LFS pointer file from standard input and write the contents of the corresponding large file to standard output. If needed, download the file’s contents from the Git LFS endpoint. The argument, if provided, is only used for a progress bar.

Smudge is typically run by Git’s smudge filter, configured by the repository’s Git attributes.

In your Git configuration or in a .lfsconfig file, you may set either or both of lfs.fetchinclude and lfs.fetchexclude to comma-separated lists of paths. If lfs.fetchinclude is defined, Git LFS pointer files will only be replaced with the contents of the corresponding Git LFS object file if their path matches one in that list, and if lfs.fetchexclude is defined, Git LFS pointer files will only be replaced with the contents of the corresponding Git LFS object file if their path does not match one in that list. Paths are matched using wildcard matching as per gitignore(5). Git LFS pointer files that are not replaced with the contents of their corresponding object files are simply copied to standard output without change.

Without any options, git lfs smudge outputs the raw Git LFS content to standard output.

Options

Arguments

  • <PATH> Working-tree path of the file being smudged (currently unused)

Flags

  • --skip Skip automatic downloading of objects on clone or pull.

    Equivalent to GIT_LFS_SKIP_SMUDGE=1. Wired up by git lfs install --skip-smudge.

Environment

GIT_LFS_SKIP_SMUDGE
Disables the smudging process. For more information, see: git-lfs-config(5)

Known bugs

On Windows, Git before 2.34.0 does not handle files in the working tree larger than 4 gigabytes. Newer versions of Git, as well as Unix versions, are unaffected.

See also

git-lfs-install(1), gitattributes(5), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-post-checkout

Name

git-lfs-post-checkout — Git post-checkout hook implementation

Synopsis

git-lfs-post-checkout [ARGS]...

Description

Git post-checkout hook implementation

Respond to Git post-checkout events. Git invokes this hook with <rev-before> <ref-after> <is-branch-checkout>. We make sure that any files which are marked as lockable by git lfs track are read-only in the working copy, if not currently locked by the local user.

Options

Arguments

  • <ARGS> Positional arguments passed by git. Not normally invoked by hand

See also

git-lfs-track(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-post-commit

Name

git-lfs-post-commit — Git post-commit hook implementation

Synopsis

git-lfs-post-commit [ARGS]...

Description

Git post-commit hook implementation

Respond to Git post-commit events. Like git lfs post-merge, we make sure that any files which are marked as lockable by git lfs track are read-only in the working copy, if not currently locked by the local user.

Upstream optimizes by only checking files changed in HEAD; we currently scan the full work tree on every commit. The result is the same, but slower on large repositories.

Options

Arguments

  • <ARGS> Positional arguments passed by git. Not normally invoked by hand

See also

git-lfs-post-merge(1), git-lfs-track(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-post-merge

Name

git-lfs-post-merge — Git post-merge hook implementation

Synopsis

git-lfs-post-merge [ARGS]...

Description

Git post-merge hook implementation

Respond to Git post-merge events. Git invokes this hook with <is-squash>. We make sure that any files which are marked as lockable by git lfs track are read-only in the working copy, if not currently locked by the local user.

Options

Arguments

  • <ARGS> Positional arguments passed by git. Not normally invoked by hand

See also

git-lfs-track(1).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.

git-lfs-config

Name

git-lfs-config — Configuration options for git-lfs

Synopsis

git-lfs-config

Description

Configuration options for git-lfs

Configuration files

git-lfs reads its configuration from any file git config -l returns — that is, the system, global, and per-repository Git config files in their usual precedence order.

A small subset of keys may also be set in a .lfsconfig file at the repository root; see LFSCONFIG for the format and the list of keys allowed there. This is useful for settings every clone of the repository should share — most commonly lfs.url or an access mode — without forcing each user to configure them manually.

If .lfsconfig is missing on disk, the index is checked for it. If that’s also missing, HEAD is checked. In a bare repository, only HEAD is checked.

Settings from Git config files override .lfsconfig. This lets you change an LFS-related setting locally (e.g. point lfs.url at a staging server) without modifying the repository’s tracked configuration.

Most LFS settings live in the [lfs] section — keys of the form lfs.<foo>. A handful are scoped inside a particular remote’s config (remote.<name>.lfsurl and similar) and override the global lfs.* equivalents for that remote.

URL-specific overrides are written as lfs.<url>.<key>, where <url> is the LFS endpoint the setting should apply to. Longest-prefix match wins, so lfs.https://lfs.example.com/.locksverify overrides lfs.locksverify only for that endpoint.

General settings

  • lfs.url / remote.<remote>.lfsurl

    The URL of the Git LFS API endpoint. Defaults to deriving the endpoint from the clone URL (<clone-url>/info/lfs). The remote-scoped form overrides the general one for a particular remote.

  • lfs.pushurl / remote.<remote>.lfspushurl

    Same idea but consulted only when pushing. Defaults to lfs.url or the derived endpoint.

  • lfs.<url>.access

    Authentication mode for the LFS endpoint at <url>. Either basic (HTTP basic auth via the credential helper, the default after a successful round-trip) or none (no authentication). Set via git config --add when the access mode for an endpoint should be persisted; the auth-retry loop also writes this on a successful 401-fill cycle.

  • core.askpass / GIT_ASKPASS

    Program invoked when interactive credentials are needed against the LFS API. Stdout is read as the credential value. Same selection priority as Git uses: GIT_ASKPASS env beats core.askpass, which beats SSH_ASKPASS.

  • credential.helper, credential.useHttpPath, credential.protectProtocol

    Standard Git credential-helper plumbing. useHttpPath=true distinguishes credentials per path within a host (so two paths on the same domain can have different passwords). protectProtocol=false lets credentials with carriage returns through (default true).

Upload and download transfer settings

  • lfs.concurrenttransfers

    Number of object transfers running in parallel within a single LFS command. Default 8.

  • lfs.basictransfersonly

    When true, restrict the client to the basic HTTP upload/download adapter, ignoring more advanced transfers the server may advertise. Useful for working around broken intermediaries. Default false.

  • lfs.transfer.batchSize

    Max objects per POST /objects/batch request. The transfer queue chunks the input list into runs of this size and issues one batch call per chunk. Default 100. Values < 1 are clamped to 1. Servers may refuse oversize batches with 413; lower this if you see those.

  • lfs.transfer.enablehrefrewrite

    When true, applies url.<base>.insteadOf / url.<base>.pushInsteadOf rewrites to the action URLs the batch endpoint hands back. pushInsteadOf is used for upload actions; insteadOf is used for downloads and for uploads when pushInsteadOf isn’t set. Default false.

  • lfs.<url>.contenttype

    When true (the default), the basic upload adapter sniffs the first 512 bytes of each object and sets the Content-Type header on the PUT to the detected MIME type. Set to false to send application/octet-stream unconditionally — useful when a CDN rejects uploads based on content sniffing. The batch response’s action.header always wins if it pins a Content-Type itself.

  • lfs.<url>.sshtransfer

    Whether to use SSH (git-lfs-authenticate) for the LFS endpoint at <url>. Values: negotiate (try SSH first, fall back to HTTPS — the default for ssh:// and git@ remotes), always, or never.

Push settings

  • lfs.allowincompletepush

    When true, allow a push to complete even if some LFS objects are missing from the local cache. By default (false), pre-push aborts and the user has to resolve the gap before pushing.

  • lfs.<url>.locksverify (or unscoped lfs.locksverify)

    Controls whether the pre-push hook calls the lock API on the LFS endpoint to refuse pushes over files locked by someone else.

    • true: verify locks; halt the push if any are violated or the server is unreachable.
    • false: skip the lock check entirely. Set this if you don’t use file locking, or your server enforces it server-side.
    • Unset: attempt the call; if it succeeds, persist true for next time. If the server returns 501 Not Implemented, persist false. If it fails for another reason, warn and continue. (Matches upstream’s first-call probe.)

Fetch settings

  • lfs.fetchinclude

    Comma-separated list of gitignore(5)-style patterns. When set, fetch only downloads objects whose path matches one of them. Empty string disables the filter.

  • lfs.fetchexclude

    Inverse of fetchinclude — fetch skips objects whose path matches.

  • lfs.fetchrecentrefsdays

    Branches whose tip commit lies within this many days of now are included by fetch --recent. Only local refs are scanned unless lfs.fetchrecentremoterefs is also set. Default 7. A value of 0 disables ref-window retention entirely.

  • lfs.fetchrecentremoterefs

    When true, fetch --recent also scans the remote-tracking refs of the remote being fetched (useful for picking up branches you might check out later without first creating a tracking local ref). Default true.

  • lfs.fetchrecentcommitsdays

    In addition to fetching the tip state of each recent ref, also fetch LFS objects referenced by commits within this many days of that ref’s tip. Default 0 (tip only).

  • lfs.fetchrecentalways

    When true, always behave as if --recent was passed. Default false.

Prune settings

  • lfs.pruneoffsetdays

    Extra days added to the lfs.fetchrecent*days windows when deciding what prune can delete. A ref or commit has to be at least this many days older than the oldest one fetch --recent would download for prune to treat it as old enough to delete. Default 3. Only takes effect when the underlying fetch-recent setting is non-zero.

  • lfs.pruneremotetocheck

    Remote to consult for UNPUSHED LFS FILES detection and --verify-remote. Default origin. See git-lfs-prune(1) for the full retention rules.

  • lfs.pruneverifyremotealways

    When true, always run prune as if --verify-remote was passed. The pre-delete remote-presence check applies on every invocation. Use --no-verify-remote to opt out for a single run.

  • lfs.pruneverifyunreachablealways

    When true, always run prune as if --verify-unreachable was passed — also verify objects not reachable from any commit. Only meaningful when remote verification is on. Use --no-verify-unreachable to opt out for a single run.

Extensions

Git LFS extensions wrap each object’s bytes through an external program on the clean (commit) and smudge (checkout) paths — useful for repository-wide transforms like compression or encryption that should happen alongside pointerization.

  • lfs.extension.<name>.clean

    Command run when files are added to the index. Receives the raw bytes on stdin and is expected to emit transformed bytes on stdout.

  • lfs.extension.<name>.smudge

    Command run when files are written into the working copy. Reverses what clean produced.

  • lfs.extension.<name>.priority

    Sort order across extensions. Lower priorities run first on the clean side, last on the smudge side (so a chain compress -> encrypt reverses to decrypt -> decompress). Required when more than one extension is configured.

See git-lfs-ext(1) for inspecting the resolved chain.

Other settings

  • lfs.setlockablereadonly / GIT_LFS_SET_LOCKABLE_READONLY

    Whether files tracked as lockable in .gitattributes are made read-only in the working copy unless the current user holds the lock. Default true. Set either to 0 / false / no to keep them writeable.

  • lfs.skipdownloaderrors / GIT_LFS_SKIP_DOWNLOAD_ERRORS

    Don’t abort the smudge filter when an LFS download fails. The pointer is left in the working tree as-is, and the surrounding git checkout (or whatever invoked smudge) reports success. Useful when you need to operate on a repository whose remote is temporarily unavailable, but be aware that scripts checking smudge exit status won’t see the failure.

  • GIT_LFS_SKIP_SMUDGE

    Skip pointer-to-content conversion in git lfs smudge and git lfs filter-process. Equivalent to running git lfs install --skip-smudge (which sets it via filter.lfs.process). Any value other than empty / 0 / false enables it.

  • GIT_LFS_SKIP_PUSH

    Make the pre-push hook a no-op. New LFS objects are not uploaded for the duration of the command. Same value semantics as GIT_LFS_SKIP_SMUDGE.

Lfsconfig

.lfsconfig at the repository root uses the same format as .git/config. Only a restricted set of keys is honored here (the others are silently ignored), for security: a .lfsconfig from an untrusted clone shouldn’t be able to override credential helpers or arbitrary Git config.

Allowed keys:

  • lfs.allowincompletepush
  • lfs.fetchexclude
  • lfs.fetchinclude
  • lfs.gitprotocol
  • lfs.locksverify
  • lfs.pushurl
  • lfs.skipdownloaderrors
  • lfs.url
  • lfs.<url>.access
  • remote.<name>.lfsurl

Examples

Configure a custom LFS endpoint for everyone who clones the repository:

git config -f .lfsconfig lfs.url https://lfs.example.com/foo/bar/info/lfs

Set the endpoint locally for the current user without touching .lfsconfig:

git config --global lfs.url https://lfs.example.com/foo/bar/info/lfs

Disable lock verification at the pre-push hook for a specific endpoint:

git config --global lfs.https://lfs.example.com/.locksverify false

Raise the concurrent-transfer ceiling on a fast link:

git config --global lfs.concurrenttransfers 16

Exclude a large media subtree from fetch/checkout:

git config lfs.fetchexclude "media/raw/**,**/*.psd"

See also

git-lfs(1), git-config(1), gitattributes(5), gitignore(5).

Reporting bugs

This command is from the Rust implementation of git-lfs, not the original Go implementation. Please report bugs to our issue tracker.