1 /*
2 * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
3 * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
4 * and other copyright owners as documented in the project's IP log.
5 *
6 * This program and the accompanying materials are made available
7 * under the terms of the Eclipse Distribution License v1.0 which
8 * accompanies this distribution, is reproduced below, and is
9 * available at http://www.eclipse.org/org/documents/edl-v10.php
10 *
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
15 * conditions are met:
16 *
17 * - Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials provided
23 * with the distribution.
24 *
25 * - Neither the name of the Eclipse Foundation, Inc. nor the
26 * names of its contributors may be used to endorse or promote
27 * products derived from this software without specific prior
28 * written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 */
44 package org.eclipse.jgit.api;
45
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.util.Collection;
49 import java.util.LinkedList;
50
51 import org.eclipse.jgit.api.errors.GitAPIException;
52 import org.eclipse.jgit.api.errors.JGitInternalException;
53 import org.eclipse.jgit.api.errors.NoFilepatternException;
54 import org.eclipse.jgit.dircache.DirCache;
55 import org.eclipse.jgit.dircache.DirCacheBuildIterator;
56 import org.eclipse.jgit.dircache.DirCacheBuilder;
57 import org.eclipse.jgit.dircache.DirCacheEntry;
58 import org.eclipse.jgit.dircache.DirCacheIterator;
59 import org.eclipse.jgit.internal.JGitText;
60 import org.eclipse.jgit.lib.Constants;
61 import org.eclipse.jgit.lib.FileMode;
62 import org.eclipse.jgit.lib.ObjectInserter;
63 import org.eclipse.jgit.lib.Repository;
64 import org.eclipse.jgit.treewalk.FileTreeIterator;
65 import org.eclipse.jgit.treewalk.TreeWalk;
66 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
67 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
68
69 /**
70 * A class used to execute a {@code Add} command. It has setters for all
71 * supported options and arguments of this command and a {@link #call()} method
72 * to finally execute the command. Each instance of this class should only be
73 * used for one invocation of the command (means: one call to {@link #call()})
74 *
75 * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-add.html"
76 * >Git documentation about Add</a>
77 */
78 public class AddCommand extends GitCommand<DirCache> {
79
80 private Collection<String> filepatterns;
81
82 private WorkingTreeIterator workingTreeIterator;
83
84 private boolean update = false;
85
86 /**
87 *
88 * @param repo
89 */
90 public AddCommand(Repository repo) {
91 super(repo);
92 filepatterns = new LinkedList<String>();
93 }
94
95 /**
96 * Add a path to a file/directory whose content should be added.
97 * <p>
98 * A directory name (e.g. <code>dir</code> to add <code>dir/file1</code> and
99 * <code>dir/file2</code>) can also be given to add all files in the
100 * directory, recursively. Fileglobs (e.g. *.c) are not yet supported.
101 *
102 * @param filepattern
103 * repository-relative path of file/directory to add (with
104 * <code>/</code> as separator)
105 * @return {@code this}
106 */
107 public AddCommand addFilepattern(String filepattern) {
108 checkCallable();
109 filepatterns.add(filepattern);
110 return this;
111 }
112
113 /**
114 * Allow clients to provide their own implementation of a FileTreeIterator
115 * @param f
116 * @return {@code this}
117 */
118 public AddCommand setWorkingTreeIterator(WorkingTreeIterator f) {
119 workingTreeIterator = f;
120 return this;
121 }
122
123 /**
124 * Executes the {@code Add} command. Each instance of this class should only
125 * be used for one invocation of the command. Don't call this method twice
126 * on an instance.
127 *
128 * @return the DirCache after Add
129 */
130 public DirCache call() throws GitAPIException, NoFilepatternException {
131
132 if (filepatterns.isEmpty())
133 throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
134 checkCallable();
135 DirCache dc = null;
136 boolean addAll = false;
137 if (filepatterns.contains(".")) //$NON-NLS-1$
138 addAll = true;
139
140 try (ObjectInserter inserter = repo.newObjectInserter();
141 final TreeWalk tw = new TreeWalk(repo)) {
142 dc = repo.lockDirCache();
143 DirCacheIterator c;
144
145 DirCacheBuilder builder = dc.builder();
146 tw.addTree(new DirCacheBuildIterator(builder));
147 if (workingTreeIterator == null)
148 workingTreeIterator = new FileTreeIterator(repo);
149 tw.addTree(workingTreeIterator);
150 tw.setRecursive(true);
151 if (!addAll)
152 tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
153
154 String lastAddedFile = null;
155
156 while (tw.next()) {
157 String path = tw.getPathString();
158
159 WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class);
160 if (tw.getTree(0, DirCacheIterator.class) == null &&
161 f != null && f.isEntryIgnored()) {
162 // file is not in index but is ignored, do nothing
163 }
164 // In case of an existing merge conflict the
165 // DirCacheBuildIterator iterates over all stages of
166 // this path, we however want to add only one
167 // new DirCacheEntry per path.
168 else if (!(path.equals(lastAddedFile))) {
169 if (!(update && tw.getTree(0, DirCacheIterator.class) == null)) {
170 c = tw.getTree(0, DirCacheIterator.class);
171 if (f != null) { // the file exists
172 long sz = f.getEntryLength();
173 DirCacheEntry entry = new DirCacheEntry(path);
174 if (c == null || c.getDirCacheEntry() == null
175 || !c.getDirCacheEntry().isAssumeValid()) {
176 FileMode mode = f.getIndexFileMode(c);
177 entry.setFileMode(mode);
178
179 if (FileMode.GITLINK != mode) {
180 entry.setLength(sz);
181 entry.setLastModified(f
182 .getEntryLastModified());
183 long contentSize = f
184 .getEntryContentLength();
185 InputStream in = f.openEntryStream();
186 try {
187 entry.setObjectId(inserter.insert(
188 Constants.OBJ_BLOB, contentSize, in));
189 } finally {
190 in.close();
191 }
192 } else
193 entry.setObjectId(f.getEntryObjectId());
194 builder.add(entry);
195 lastAddedFile = path;
196 } else {
197 builder.add(c.getDirCacheEntry());
198 }
199
200 } else if (c != null
201 && (!update || FileMode.GITLINK == c
202 .getEntryFileMode()))
203 builder.add(c.getDirCacheEntry());
204 }
205 }
206 }
207 inserter.flush();
208 builder.commit();
209 setCallable(false);
210 } catch (IOException e) {
211 throw new JGitInternalException(
212 JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
213 } finally {
214 if (dc != null)
215 dc.unlock();
216 }
217
218 return dc;
219 }
220
221 /**
222 * @param update
223 * If set to true, the command only matches {@code filepattern}
224 * against already tracked files in the index rather than the
225 * working tree. That means that it will never stage new files,
226 * but that it will stage modified new contents of tracked files
227 * and that it will remove files from the index if the
228 * corresponding files in the working tree have been removed.
229 * In contrast to the git command line a {@code filepattern} must
230 * exist also if update is set to true as there is no
231 * concept of a working directory here.
232 *
233 * @return {@code this}
234 */
235 public AddCommand setUpdate(boolean update) {
236 this.update = update;
237 return this;
238 }
239
240 /**
241 * @return is the parameter update is set
242 */
243 public boolean isUpdate() {
244 return update;
245 }
246 }