All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.jgit.api.AddCommand Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2010, Christian Halstrick 
 * Copyright (C) 2010, Stefan Lay  and others
 *
 * 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.openrewrite.jgit.api;

import static org.openrewrite.jgit.lib.Constants.OBJ_BLOB;
import static org.openrewrite.jgit.lib.FileMode.GITLINK;
import static org.openrewrite.jgit.lib.FileMode.TYPE_GITLINK;
import static org.openrewrite.jgit.lib.FileMode.TYPE_TREE;

import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.Collection;
import java.util.LinkedList;

import org.openrewrite.jgit.api.errors.FilterFailedException;
import org.openrewrite.jgit.api.errors.GitAPIException;
import org.openrewrite.jgit.api.errors.JGitInternalException;
import org.openrewrite.jgit.api.errors.NoFilepatternException;
import org.openrewrite.jgit.dircache.DirCache;
import org.openrewrite.jgit.dircache.DirCacheBuildIterator;
import org.openrewrite.jgit.dircache.DirCacheBuilder;
import org.openrewrite.jgit.dircache.DirCacheEntry;
import org.openrewrite.jgit.dircache.DirCacheIterator;
import org.openrewrite.jgit.internal.JGitText;
import org.openrewrite.jgit.lib.FileMode;
import org.openrewrite.jgit.lib.ObjectId;
import org.openrewrite.jgit.lib.ObjectInserter;
import org.openrewrite.jgit.lib.Repository;
import org.openrewrite.jgit.treewalk.FileTreeIterator;
import org.openrewrite.jgit.treewalk.NameConflictTreeWalk;
import org.openrewrite.jgit.treewalk.TreeWalk.OperationType;
import org.openrewrite.jgit.treewalk.WorkingTreeIterator;
import org.openrewrite.jgit.treewalk.filter.PathFilterGroup;

/**
 * A class used to execute a {@code Add} command. It has setters for all
 * supported options and arguments of this command and a {@link #call()} method
 * to finally execute the command. Each instance of this class should only be
 * used for one invocation of the command (means: one call to {@link #call()})
 *
 * @see Git documentation about Add
 */
public class AddCommand extends GitCommand {

	private Collection filepatterns;

	private WorkingTreeIterator workingTreeIterator;

	private boolean update = false;

	/**
	 * Constructor for AddCommand
	 *
	 * @param repo
	 *            the {@link org.openrewrite.jgit.lib.Repository}
	 */
	public AddCommand(Repository repo) {
		super(repo);
		filepatterns = new LinkedList<>();
	}

	/**
	 * Add a path to a file/directory whose content should be added.
	 * 

* A directory name (e.g. dir to add dir/file1 and * dir/file2) can also be given to add all files in the * directory, recursively. Fileglobs (e.g. *.c) are not yet supported. * * @param filepattern * repository-relative path of file/directory to add (with * / as separator) * @return {@code this} */ public AddCommand addFilepattern(String filepattern) { checkCallable(); filepatterns.add(filepattern); return this; } /** * Allow clients to provide their own implementation of a FileTreeIterator * * @param f * a {@link org.openrewrite.jgit.treewalk.WorkingTreeIterator} * object. * @return {@code this} */ public AddCommand setWorkingTreeIterator(WorkingTreeIterator f) { workingTreeIterator = f; return this; } /** * {@inheritDoc} *

* Executes the {@code Add} command. Each instance of this class should only * be used for one invocation of the command. Don't call this method twice * on an instance. */ @Override public DirCache call() throws GitAPIException, NoFilepatternException { if (filepatterns.isEmpty()) throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired); checkCallable(); DirCache dc = null; boolean addAll = filepatterns.contains("."); //$NON-NLS-1$ try (ObjectInserter inserter = repo.newObjectInserter(); NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) { tw.setOperationType(OperationType.CHECKIN_OP); dc = repo.lockDirCache(); DirCacheBuilder builder = dc.builder(); tw.addTree(new DirCacheBuildIterator(builder)); if (workingTreeIterator == null) workingTreeIterator = new FileTreeIterator(repo); workingTreeIterator.setDirCacheIterator(tw, 0); tw.addTree(workingTreeIterator); if (!addAll) tw.setFilter(PathFilterGroup.createFromStrings(filepatterns)); byte[] lastAdded = null; while (tw.next()) { DirCacheIterator c = tw.getTree(0, DirCacheIterator.class); WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class); if (c == null && f != null && f.isEntryIgnored()) { // file is not in index but is ignored, do nothing continue; } else if (c == null && update) { // Only update of existing entries was requested. continue; } DirCacheEntry entry = c != null ? c.getDirCacheEntry() : null; if (entry != null && entry.getStage() > 0 && lastAdded != null && lastAdded.length == tw.getPathLength() && tw.isPathPrefix(lastAdded, lastAdded.length) == 0) { // In case of an existing merge conflict the // DirCacheBuildIterator iterates over all stages of // this path, we however want to add only one // new DirCacheEntry per path. continue; } if (tw.isSubtree() && !tw.isDirectoryFileConflict()) { tw.enterSubtree(); continue; } if (f == null) { // working tree file does not exist if (entry != null && (!update || GITLINK == entry.getFileMode())) { builder.add(entry); } continue; } if (entry != null && entry.isAssumeValid()) { // Index entry is marked assume valid. Even though // the user specified the file to be added JGit does // not consider the file for addition. builder.add(entry); continue; } if ((f.getEntryRawMode() == TYPE_TREE && f.getIndexFileMode(c) != FileMode.GITLINK) || (f.getEntryRawMode() == TYPE_GITLINK && f.getIndexFileMode(c) == FileMode.TREE)) { // Index entry exists and is symlink, gitlink or file, // otherwise the tree would have been entered above. // Replace the index entry by diving into tree of files. tw.enterSubtree(); continue; } byte[] path = tw.getRawPath(); if (entry == null || entry.getStage() > 0) { entry = new DirCacheEntry(path); } FileMode mode = f.getIndexFileMode(c); entry.setFileMode(mode); if (GITLINK != mode) { entry.setLength(f.getEntryLength()); entry.setLastModified(f.getEntryLastModifiedInstant()); long len = f.getEntryContentLength(); // We read and filter the content multiple times. // f.getEntryContentLength() reads and filters the input and // inserter.insert(...) does it again. That's because an // ObjectInserter needs to know the length before it starts // inserting. TODO: Fix this by using Buffers. try (InputStream in = f.openEntryStream()) { ObjectId id = inserter.insert(OBJ_BLOB, len, in); entry.setObjectId(id); } } else { entry.setLength(0); entry.setLastModified(Instant.ofEpochSecond(0)); entry.setObjectId(f.getEntryObjectId()); } builder.add(entry); lastAdded = path; } inserter.flush(); builder.commit(); setCallable(false); } catch (IOException e) { Throwable cause = e.getCause(); if (cause != null && cause instanceof FilterFailedException) throw (FilterFailedException) cause; throw new JGitInternalException( JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e); } finally { if (dc != null) dc.unlock(); } return dc; } /** * Set whether to only match against already tracked files * * @param update * If set to true, the command only matches {@code filepattern} * against already tracked files in the index rather than the * working tree. That means that it will never stage new files, * but that it will stage modified new contents of tracked files * and that it will remove files from the index if the * corresponding files in the working tree have been removed. In * contrast to the git command line a {@code filepattern} must * exist also if update is set to true as there is no concept of * a working directory here. * @return {@code this} */ public AddCommand setUpdate(boolean update) { this.update = update; return this; } /** * Whether to only match against already tracked files * * @return whether to only match against already tracked files */ public boolean isUpdate() { return update; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy