| /* | |
| * Copyright (C) 2022, Matthias Fromme <mfromme@dspace.de> | |
| * | |
| * This program and the accompanying materials are made available under the | |
| * terms of the Eclipse Distribution License v. 1.0 which is available at | |
| * https://www.eclipse.org/org/documents/edl-v10.php. | |
| * | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| */ | |
| package org.eclipse.jgit.lfs.internal; | |
| import java.io.File; | |
| import java.io.IOException; | |
| import org.eclipse.jgit.annotations.Nullable; | |
| import org.eclipse.jgit.dircache.DirCacheEntry; | |
| import org.eclipse.jgit.errors.ConfigInvalidException; | |
| import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException; | |
| import org.eclipse.jgit.lfs.lib.Constants; | |
| import org.eclipse.jgit.lib.BlobBasedConfig; | |
| import org.eclipse.jgit.lib.Config; | |
| import org.eclipse.jgit.lib.ObjectId; | |
| import org.eclipse.jgit.lib.Repository; | |
| import org.eclipse.jgit.revwalk.RevCommit; | |
| import org.eclipse.jgit.revwalk.RevTree; | |
| import org.eclipse.jgit.revwalk.RevWalk; | |
| import org.eclipse.jgit.storage.file.FileBasedConfig; | |
| import org.eclipse.jgit.treewalk.TreeWalk; | |
| import static org.eclipse.jgit.lib.Constants.HEAD; | |
| /** | |
| * Encapsulate access to the {@code .lfsconfig}. | |
| * <p> | |
| * According to the git lfs documentation the order to find the | |
| * {@code .lfsconfig} file is: | |
| * </p> | |
| * <ol> | |
| * <li>in the root of the working tree</li> | |
| * <li>in the index</li> | |
| * <li>in the HEAD; for bare repositories this is the only place that is | |
| * searched</li> | |
| * </ol> | |
| * <p> | |
| * Values from the {@code .lfsconfig} are used only if not specified in another | |
| * git config file to allow local override without modifiction of a committed | |
| * file. | |
| * </p> | |
| * | |
| * @see <a href= | |
| * "https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.5.ronn">Configuration | |
| * options for git-lfs</a> | |
| */ | |
| public class LfsConfig { | |
| private Repository db; | |
| private Config delegate; | |
| /** | |
| * Create a new instance of the LfsConfig. | |
| * | |
| * @param db | |
| * the associated repo | |
| */ | |
| public LfsConfig(Repository db) { | |
| this.db = db; | |
| } | |
| /** | |
| * Getter for the delegate to allow lazy initialization. | |
| * | |
| * @return the delegate {@link Config} | |
| * @throws IOException | |
| * if an IO error occurred | |
| */ | |
| private Config getDelegate() throws IOException { | |
| if (delegate == null) { | |
| delegate = this.load(); | |
| } | |
| return delegate; | |
| } | |
| /** | |
| * Read the .lfsconfig file from the repository | |
| * | |
| * An empty config is returned be empty if no lfs config exists. | |
| * | |
| * @return The loaded lfs config | |
| * | |
| * @throws IOException | |
| * if an IO error occurred | |
| */ | |
| private Config load() throws IOException { | |
| Config result = null; | |
| if (!db.isBare()) { | |
| result = loadFromWorkingTree(); | |
| if (result == null) { | |
| result = loadFromIndex(); | |
| } | |
| } | |
| if (result == null) { | |
| result = loadFromHead(); | |
| } | |
| if (result == null) { | |
| result = emptyConfig(); | |
| } | |
| return result; | |
| } | |
| /** | |
| * Try to read the lfs config from a file called .lfsconfig at the top level | |
| * of the working tree. | |
| * | |
| * @return the config, or <code>null</code> | |
| * @throws IOException | |
| * if an IO error occurred | |
| */ | |
| @Nullable | |
| private Config loadFromWorkingTree() | |
| throws IOException { | |
| File lfsConfig = db.getFS().resolve(db.getWorkTree(), | |
| Constants.DOT_LFS_CONFIG); | |
| if (lfsConfig.isFile()) { | |
| FileBasedConfig config = new FileBasedConfig(lfsConfig, db.getFS()); | |
| try { | |
| config.load(); | |
| return config; | |
| } catch (ConfigInvalidException e) { | |
| throw new LfsConfigInvalidException( | |
| LfsText.get().dotLfsConfigReadFailed, e); | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Try to read the lfs config from an entry called .lfsconfig contained in | |
| * the index. | |
| * | |
| * @return the config, or <code>null</code> if the entry does not exist | |
| * @throws IOException | |
| * if an IO error occurred | |
| */ | |
| @Nullable | |
| private Config loadFromIndex() | |
| throws IOException { | |
| try { | |
| DirCacheEntry entry = db.readDirCache() | |
| .getEntry(Constants.DOT_LFS_CONFIG); | |
| if (entry != null) { | |
| return new BlobBasedConfig(null, db, entry.getObjectId()); | |
| } | |
| } catch (ConfigInvalidException e) { | |
| throw new LfsConfigInvalidException( | |
| LfsText.get().dotLfsConfigReadFailed, e); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Try to read the lfs config from an entry called .lfsconfig contained in | |
| * the head revision. | |
| * | |
| * @return the config, or <code>null</code> if the file does not exist | |
| * @throws IOException | |
| * if an IO error occurred | |
| */ | |
| @Nullable | |
| private Config loadFromHead() throws IOException { | |
| try (RevWalk revWalk = new RevWalk(db)) { | |
| ObjectId headCommitId = db.resolve(HEAD); | |
| if (headCommitId == null) { | |
| return null; | |
| } | |
| RevCommit commit = revWalk.parseCommit(headCommitId); | |
| RevTree tree = commit.getTree(); | |
| TreeWalk treewalk = TreeWalk.forPath(db, Constants.DOT_LFS_CONFIG, | |
| tree); | |
| if (treewalk != null) { | |
| return new BlobBasedConfig(null, db, treewalk.getObjectId(0)); | |
| } | |
| } catch (ConfigInvalidException e) { | |
| throw new LfsConfigInvalidException( | |
| LfsText.get().dotLfsConfigReadFailed, e); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Create an empty config as fallback to avoid null pointer checks. | |
| * | |
| * @return an empty config | |
| */ | |
| private Config emptyConfig() { | |
| return new Config(); | |
| } | |
| /** | |
| * Get string value or null if not found. | |
| * | |
| * First tries to find the value in the git config files. If not found tries | |
| * to find data in .lfsconfig. | |
| * | |
| * @param section | |
| * the section | |
| * @param subsection | |
| * the subsection for the value | |
| * @param name | |
| * the key name | |
| * @return a String value from the config, <code>null</code> if not found | |
| * @throws IOException | |
| * if an IO error occurred | |
| */ | |
| @Nullable | |
| public String getString(final String section, final String subsection, | |
| final String name) throws IOException { | |
| String result = db.getConfig().getString(section, subsection, name); | |
| if (result == null) { | |
| result = getDelegate().getString(section, subsection, name); | |
| } | |
| return result; | |
| } | |
| } |