Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

AddCommandTest.java 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308
  1. /*
  2. * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
  3. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@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. import static org.eclipse.jgit.util.FileUtils.RECURSIVE;
  46. import static org.junit.Assert.assertEquals;
  47. import static org.junit.Assert.assertTrue;
  48. import static org.junit.Assert.fail;
  49. import java.io.File;
  50. import java.io.FileInputStream;
  51. import java.io.IOException;
  52. import java.io.PrintWriter;
  53. import java.util.Set;
  54. import org.eclipse.jgit.api.errors.FilterFailedException;
  55. import org.eclipse.jgit.api.errors.GitAPIException;
  56. import org.eclipse.jgit.api.errors.NoFilepatternException;
  57. import org.eclipse.jgit.attributes.FilterCommandRegistry;
  58. import org.eclipse.jgit.dircache.DirCache;
  59. import org.eclipse.jgit.dircache.DirCacheBuilder;
  60. import org.eclipse.jgit.dircache.DirCacheEntry;
  61. import org.eclipse.jgit.junit.JGitTestUtil;
  62. import org.eclipse.jgit.junit.RepositoryTestCase;
  63. import org.eclipse.jgit.lfs.CleanFilter;
  64. import org.eclipse.jgit.lfs.SmudgeFilter;
  65. import org.eclipse.jgit.lib.ConfigConstants;
  66. import org.eclipse.jgit.lib.Constants;
  67. import org.eclipse.jgit.lib.FileMode;
  68. import org.eclipse.jgit.lib.ObjectId;
  69. import org.eclipse.jgit.lib.ObjectInserter;
  70. import org.eclipse.jgit.lib.Repository;
  71. import org.eclipse.jgit.lib.StoredConfig;
  72. import org.eclipse.jgit.revwalk.RevCommit;
  73. import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
  74. import org.eclipse.jgit.treewalk.TreeWalk;
  75. import org.eclipse.jgit.treewalk.WorkingTreeOptions;
  76. import org.eclipse.jgit.util.FS;
  77. import org.eclipse.jgit.util.FileUtils;
  78. import org.junit.Test;
  79. import org.junit.experimental.theories.DataPoints;
  80. import org.junit.experimental.theories.Theories;
  81. import org.junit.experimental.theories.Theory;
  82. import org.junit.runner.RunWith;
  83. @RunWith(Theories.class)
  84. public class AddCommandTest extends RepositoryTestCase {
  85. @DataPoints
  86. public static boolean[] sleepBeforeAddOptions = { true, false };
  87. @Override
  88. public void setUp() throws Exception {
  89. CleanFilter.register();
  90. SmudgeFilter.register();
  91. super.setUp();
  92. }
  93. @Test
  94. public void testAddNothing() throws GitAPIException {
  95. try (Git git = new Git(db)) {
  96. git.add().call();
  97. fail("Expected IllegalArgumentException");
  98. } catch (NoFilepatternException e) {
  99. // expected
  100. }
  101. }
  102. @Test
  103. public void testAddNonExistingSingleFile() throws GitAPIException {
  104. try (Git git = new Git(db)) {
  105. DirCache dc = git.add().addFilepattern("a.txt").call();
  106. assertEquals(0, dc.getEntryCount());
  107. }
  108. }
  109. @Test
  110. public void testAddExistingSingleFile() throws IOException, GitAPIException {
  111. File file = new File(db.getWorkTree(), "a.txt");
  112. FileUtils.createNewFile(file);
  113. PrintWriter writer = new PrintWriter(file);
  114. writer.print("content");
  115. writer.close();
  116. try (Git git = new Git(db)) {
  117. git.add().addFilepattern("a.txt").call();
  118. assertEquals(
  119. "[a.txt, mode:100644, content:content]",
  120. indexState(CONTENT));
  121. }
  122. }
  123. @Test
  124. public void testCleanFilter() throws IOException, GitAPIException {
  125. writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
  126. writeTrashFile("src/a.tmp", "foo");
  127. // Caution: we need a trailing '\n' since sed on mac always appends
  128. // linefeeds if missing
  129. writeTrashFile("src/a.txt", "foo\n");
  130. File script = writeTempFile("sed s/o/e/g");
  131. try (Git git = new Git(db)) {
  132. StoredConfig config = git.getRepository().getConfig();
  133. config.setString("filter", "tstFilter", "clean",
  134. "sh " + slashify(script.getPath()));
  135. config.save();
  136. git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
  137. .call();
  138. assertEquals(
  139. "[src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:fee\n]",
  140. indexState(CONTENT));
  141. }
  142. }
  143. @Theory
  144. public void testBuiltinFilters(boolean sleepBeforeAdd)
  145. throws IOException,
  146. GitAPIException, InterruptedException {
  147. writeTrashFile(".gitattributes", "*.txt filter=lfs");
  148. writeTrashFile("src/a.tmp", "foo");
  149. // Caution: we need a trailing '\n' since sed on mac always appends
  150. // linefeeds if missing
  151. File script = writeTempFile("sed s/o/e/g");
  152. File f = writeTrashFile("src/a.txt", "foo\n");
  153. try (Git git = new Git(db)) {
  154. if (!sleepBeforeAdd) {
  155. fsTick(f);
  156. }
  157. git.add().addFilepattern(".gitattributes").call();
  158. StoredConfig config = git.getRepository().getConfig();
  159. config.setString("filter", "lfs", "clean",
  160. "sh " + slashify(script.getPath()));
  161. config.setString("filter", "lfs", "smudge",
  162. "sh " + slashify(script.getPath()));
  163. config.setBoolean("filter", "lfs", "useJGitBuiltin", true);
  164. config.save();
  165. if (!sleepBeforeAdd) {
  166. fsTick(f);
  167. }
  168. git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
  169. .addFilepattern(".gitattributes").call();
  170. assertEquals(
  171. "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
  172. indexState(CONTENT));
  173. RevCommit c1 = git.commit().setMessage("c1").call();
  174. assertTrue(git.status().call().isClean());
  175. f = writeTrashFile("src/a.txt", "foobar\n");
  176. if (!sleepBeforeAdd) {
  177. fsTick(f);
  178. }
  179. git.add().addFilepattern("src/a.txt").call();
  180. git.commit().setMessage("c2").call();
  181. assertTrue(git.status().call().isClean());
  182. assertEquals(
  183. "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f\nsize 7\n]",
  184. indexState(CONTENT));
  185. assertEquals("foobar\n", read("src/a.txt"));
  186. git.checkout().setName(c1.getName()).call();
  187. assertEquals(
  188. "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
  189. indexState(CONTENT));
  190. assertEquals(
  191. "foo\n", read("src/a.txt"));
  192. }
  193. }
  194. @Theory
  195. public void testBuiltinCleanFilter(boolean sleepBeforeAdd)
  196. throws IOException, GitAPIException, InterruptedException {
  197. writeTrashFile(".gitattributes", "*.txt filter=lfs");
  198. writeTrashFile("src/a.tmp", "foo");
  199. // Caution: we need a trailing '\n' since sed on mac always appends
  200. // linefeeds if missing
  201. File script = writeTempFile("sed s/o/e/g");
  202. File f = writeTrashFile("src/a.txt", "foo\n");
  203. // unregister the smudge filter. Only clean filter should be builtin
  204. FilterCommandRegistry.unregister(
  205. org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
  206. + "lfs/smudge");
  207. try (Git git = new Git(db)) {
  208. if (!sleepBeforeAdd) {
  209. fsTick(f);
  210. }
  211. git.add().addFilepattern(".gitattributes").call();
  212. StoredConfig config = git.getRepository().getConfig();
  213. config.setString("filter", "lfs", "clean",
  214. "sh " + slashify(script.getPath()));
  215. config.setString("filter", "lfs", "smudge",
  216. "sh " + slashify(script.getPath()));
  217. config.setBoolean("filter", "lfs", "useJGitBuiltin", true);
  218. config.save();
  219. if (!sleepBeforeAdd) {
  220. fsTick(f);
  221. }
  222. git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
  223. .addFilepattern(".gitattributes").call();
  224. assertEquals(
  225. "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
  226. indexState(CONTENT));
  227. RevCommit c1 = git.commit().setMessage("c1").call();
  228. assertTrue(git.status().call().isClean());
  229. f = writeTrashFile("src/a.txt", "foobar\n");
  230. if (!sleepBeforeAdd) {
  231. fsTick(f);
  232. }
  233. git.add().addFilepattern("src/a.txt").call();
  234. git.commit().setMessage("c2").call();
  235. assertTrue(git.status().call().isClean());
  236. assertEquals(
  237. "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f\nsize 7\n]",
  238. indexState(CONTENT));
  239. assertEquals("foobar\n", read("src/a.txt"));
  240. git.checkout().setName(c1.getName()).call();
  241. assertEquals(
  242. "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
  243. indexState(CONTENT));
  244. // due to lfs clean filter but dummy smudge filter we expect strange
  245. // content. The smudge filter converts from real content to pointer
  246. // file content (starting with "version ") but the smudge filter
  247. // replaces 'o' by 'e' which results in a text starting with
  248. // "versien "
  249. assertEquals(
  250. "versien https://git-lfs.github.cem/spec/v1\neid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n",
  251. read("src/a.txt"));
  252. }
  253. }
  254. @Test
  255. public void testAttributesWithTreeWalkFilter()
  256. throws IOException, GitAPIException {
  257. writeTrashFile(".gitattributes", "*.txt filter=lfs");
  258. writeTrashFile("src/a.tmp", "foo");
  259. writeTrashFile("src/a.txt", "foo\n");
  260. File script = writeTempFile("sed s/o/e/g");
  261. try (Git git = new Git(db)) {
  262. StoredConfig config = git.getRepository().getConfig();
  263. config.setString("filter", "lfs", "clean",
  264. "sh " + slashify(script.getPath()));
  265. config.save();
  266. git.add().addFilepattern(".gitattributes").call();
  267. git.commit().setMessage("attr").call();
  268. git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
  269. .addFilepattern(".gitattributes").call();
  270. git.commit().setMessage("c1").call();
  271. assertTrue(git.status().call().isClean());
  272. }
  273. }
  274. @Test
  275. public void testAttributesConflictingMatch() throws Exception {
  276. writeTrashFile(".gitattributes", "foo/** crlf=input\n*.jar binary");
  277. writeTrashFile("foo/bar.jar", "\r\n");
  278. // We end up with attributes [binary -diff -merge -text crlf=input].
  279. // crlf should have no effect when -text is present.
  280. try (Git git = new Git(db)) {
  281. git.add().addFilepattern(".").call();
  282. assertEquals(
  283. "[.gitattributes, mode:100644, content:foo/** crlf=input\n*.jar binary]"
  284. + "[foo/bar.jar, mode:100644, content:\r\n]",
  285. indexState(CONTENT));
  286. }
  287. }
  288. @Test
  289. public void testCleanFilterEnvironment()
  290. throws IOException, GitAPIException {
  291. writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
  292. writeTrashFile("src/a.txt", "foo");
  293. File script = writeTempFile("echo $GIT_DIR; echo 1 >xyz");
  294. try (Git git = new Git(db)) {
  295. StoredConfig config = git.getRepository().getConfig();
  296. config.setString("filter", "tstFilter", "clean",
  297. "sh " + slashify(script.getPath()));
  298. config.save();
  299. git.add().addFilepattern("src/a.txt").call();
  300. String gitDir = db.getDirectory().getAbsolutePath();
  301. assertEquals("[src/a.txt, mode:100644, content:" + gitDir
  302. + "\n]", indexState(CONTENT));
  303. assertTrue(new File(db.getWorkTree(), "xyz").exists());
  304. }
  305. }
  306. @Test
  307. public void testMultipleCleanFilter() throws IOException, GitAPIException {
  308. writeTrashFile(".gitattributes",
  309. "*.txt filter=tstFilter\n*.tmp filter=tstFilter2");
  310. // Caution: we need a trailing '\n' since sed on mac always appends
  311. // linefeeds if missing
  312. writeTrashFile("src/a.tmp", "foo\n");
  313. writeTrashFile("src/a.txt", "foo\n");
  314. File script = writeTempFile("sed s/o/e/g");
  315. File script2 = writeTempFile("sed s/f/x/g");
  316. try (Git git = new Git(db)) {
  317. StoredConfig config = git.getRepository().getConfig();
  318. config.setString("filter", "tstFilter", "clean",
  319. "sh " + slashify(script.getPath()));
  320. config.setString("filter", "tstFilter2", "clean",
  321. "sh " + slashify(script2.getPath()));
  322. config.save();
  323. git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
  324. .call();
  325. assertEquals(
  326. "[src/a.tmp, mode:100644, content:xoo\n][src/a.txt, mode:100644, content:fee\n]",
  327. indexState(CONTENT));
  328. // TODO: multiple clean filters for one file???
  329. }
  330. }
  331. /**
  332. * The path of an added file name contains ';' and afterwards malicious
  333. * commands. Make sure when calling filter commands to properly escape the
  334. * filenames
  335. *
  336. * @throws IOException
  337. * @throws GitAPIException
  338. */
  339. @Test
  340. public void testCommandInjection() throws IOException, GitAPIException {
  341. // Caution: we need a trailing '\n' since sed on mac always appends
  342. // linefeeds if missing
  343. writeTrashFile("; echo virus", "foo\n");
  344. File script = writeTempFile("sed s/o/e/g");
  345. try (Git git = new Git(db)) {
  346. StoredConfig config = git.getRepository().getConfig();
  347. config.setString("filter", "tstFilter", "clean",
  348. "sh " + slashify(script.getPath()) + " %f");
  349. writeTrashFile(".gitattributes", "* filter=tstFilter");
  350. git.add().addFilepattern("; echo virus").call();
  351. // Without proper escaping the content would be "feovirus". The sed
  352. // command and the "echo virus" would contribute to the content
  353. assertEquals("[; echo virus, mode:100644, content:fee\n]",
  354. indexState(CONTENT));
  355. }
  356. }
  357. @Test
  358. public void testBadCleanFilter() throws IOException, GitAPIException {
  359. writeTrashFile("a.txt", "foo");
  360. File script = writeTempFile("sedfoo s/o/e/g");
  361. try (Git git = new Git(db)) {
  362. StoredConfig config = git.getRepository().getConfig();
  363. config.setString("filter", "tstFilter", "clean",
  364. "sh " + script.getPath());
  365. config.save();
  366. writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
  367. try {
  368. git.add().addFilepattern("a.txt").call();
  369. fail("Didn't received the expected exception");
  370. } catch (FilterFailedException e) {
  371. assertEquals(127, e.getReturnCode());
  372. }
  373. }
  374. }
  375. @Test
  376. public void testBadCleanFilter2() throws IOException, GitAPIException {
  377. writeTrashFile("a.txt", "foo");
  378. File script = writeTempFile("sed s/o/e/g");
  379. try (Git git = new Git(db)) {
  380. StoredConfig config = git.getRepository().getConfig();
  381. config.setString("filter", "tstFilter", "clean",
  382. "shfoo " + script.getPath());
  383. config.save();
  384. writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
  385. try {
  386. git.add().addFilepattern("a.txt").call();
  387. fail("Didn't received the expected exception");
  388. } catch (FilterFailedException e) {
  389. assertEquals(127, e.getReturnCode());
  390. }
  391. }
  392. }
  393. @Test
  394. public void testCleanFilterReturning12() throws IOException,
  395. GitAPIException {
  396. writeTrashFile("a.txt", "foo");
  397. File script = writeTempFile("exit 12");
  398. try (Git git = new Git(db)) {
  399. StoredConfig config = git.getRepository().getConfig();
  400. config.setString("filter", "tstFilter", "clean",
  401. "sh " + slashify(script.getPath()));
  402. config.save();
  403. writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
  404. try {
  405. git.add().addFilepattern("a.txt").call();
  406. fail("Didn't received the expected exception");
  407. } catch (FilterFailedException e) {
  408. assertEquals(12, e.getReturnCode());
  409. }
  410. }
  411. }
  412. @Test
  413. public void testNotApplicableFilter() throws IOException, GitAPIException {
  414. writeTrashFile("a.txt", "foo");
  415. File script = writeTempFile("sed s/o/e/g");
  416. try (Git git = new Git(db)) {
  417. StoredConfig config = git.getRepository().getConfig();
  418. config.setString("filter", "tstFilter", "something",
  419. "sh " + script.getPath());
  420. config.save();
  421. writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
  422. git.add().addFilepattern("a.txt").call();
  423. assertEquals("[a.txt, mode:100644, content:foo]",
  424. indexState(CONTENT));
  425. }
  426. }
  427. private File writeTempFile(String body) throws IOException {
  428. File f = File.createTempFile("AddCommandTest_", "");
  429. JGitTestUtil.write(f, body);
  430. return f;
  431. }
  432. @Test
  433. public void testAddExistingSingleSmallFileWithNewLine() throws IOException,
  434. GitAPIException {
  435. File file = new File(db.getWorkTree(), "a.txt");
  436. FileUtils.createNewFile(file);
  437. PrintWriter writer = new PrintWriter(file);
  438. writer.print("row1\r\nrow2");
  439. writer.close();
  440. try (Git git = new Git(db)) {
  441. db.getConfig().setString("core", null, "autocrlf", "false");
  442. git.add().addFilepattern("a.txt").call();
  443. assertEquals("[a.txt, mode:100644, content:row1\r\nrow2]",
  444. indexState(CONTENT));
  445. db.getConfig().setString("core", null, "autocrlf", "true");
  446. git.add().addFilepattern("a.txt").call();
  447. assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
  448. indexState(CONTENT));
  449. db.getConfig().setString("core", null, "autocrlf", "input");
  450. git.add().addFilepattern("a.txt").call();
  451. assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
  452. indexState(CONTENT));
  453. }
  454. }
  455. @Test
  456. public void testAddExistingSingleMediumSizeFileWithNewLine()
  457. throws IOException, GitAPIException {
  458. File file = new File(db.getWorkTree(), "a.txt");
  459. FileUtils.createNewFile(file);
  460. StringBuilder data = new StringBuilder();
  461. for (int i = 0; i < 1000; ++i) {
  462. data.append("row1\r\nrow2");
  463. }
  464. String crData = data.toString();
  465. PrintWriter writer = new PrintWriter(file);
  466. writer.print(crData);
  467. writer.close();
  468. String lfData = data.toString().replaceAll("\r", "");
  469. try (Git git = new Git(db)) {
  470. db.getConfig().setString("core", null, "autocrlf", "false");
  471. git.add().addFilepattern("a.txt").call();
  472. assertEquals("[a.txt, mode:100644, content:" + data + "]",
  473. indexState(CONTENT));
  474. db.getConfig().setString("core", null, "autocrlf", "true");
  475. git.add().addFilepattern("a.txt").call();
  476. assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
  477. indexState(CONTENT));
  478. db.getConfig().setString("core", null, "autocrlf", "input");
  479. git.add().addFilepattern("a.txt").call();
  480. assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
  481. indexState(CONTENT));
  482. }
  483. }
  484. @Test
  485. public void testAddExistingSingleBinaryFile() throws IOException,
  486. GitAPIException {
  487. File file = new File(db.getWorkTree(), "a.txt");
  488. FileUtils.createNewFile(file);
  489. PrintWriter writer = new PrintWriter(file);
  490. writer.print("row1\r\nrow2\u0000");
  491. writer.close();
  492. try (Git git = new Git(db)) {
  493. db.getConfig().setString("core", null, "autocrlf", "false");
  494. git.add().addFilepattern("a.txt").call();
  495. assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
  496. indexState(CONTENT));
  497. db.getConfig().setString("core", null, "autocrlf", "true");
  498. git.add().addFilepattern("a.txt").call();
  499. assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
  500. indexState(CONTENT));
  501. db.getConfig().setString("core", null, "autocrlf", "input");
  502. git.add().addFilepattern("a.txt").call();
  503. assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
  504. indexState(CONTENT));
  505. }
  506. }
  507. @Test
  508. public void testAddExistingSingleFileInSubDir() throws IOException,
  509. GitAPIException {
  510. FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
  511. File file = new File(db.getWorkTree(), "sub/a.txt");
  512. FileUtils.createNewFile(file);
  513. PrintWriter writer = new PrintWriter(file);
  514. writer.print("content");
  515. writer.close();
  516. try (Git git = new Git(db)) {
  517. git.add().addFilepattern("sub/a.txt").call();
  518. assertEquals(
  519. "[sub/a.txt, mode:100644, content:content]",
  520. indexState(CONTENT));
  521. }
  522. }
  523. @Test
  524. public void testAddExistingSingleFileTwice() throws IOException,
  525. GitAPIException {
  526. File file = new File(db.getWorkTree(), "a.txt");
  527. FileUtils.createNewFile(file);
  528. PrintWriter writer = new PrintWriter(file);
  529. writer.print("content");
  530. writer.close();
  531. try (Git git = new Git(db)) {
  532. DirCache dc = git.add().addFilepattern("a.txt").call();
  533. dc.getEntry(0).getObjectId();
  534. writer = new PrintWriter(file);
  535. writer.print("other content");
  536. writer.close();
  537. dc = git.add().addFilepattern("a.txt").call();
  538. assertEquals(
  539. "[a.txt, mode:100644, content:other content]",
  540. indexState(CONTENT));
  541. }
  542. }
  543. @Test
  544. public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
  545. File file = new File(db.getWorkTree(), "a.txt");
  546. FileUtils.createNewFile(file);
  547. PrintWriter writer = new PrintWriter(file);
  548. writer.print("content");
  549. writer.close();
  550. try (Git git = new Git(db)) {
  551. DirCache dc = git.add().addFilepattern("a.txt").call();
  552. dc.getEntry(0).getObjectId();
  553. git.commit().setMessage("commit a.txt").call();
  554. writer = new PrintWriter(file);
  555. writer.print("other content");
  556. writer.close();
  557. dc = git.add().addFilepattern("a.txt").call();
  558. assertEquals(
  559. "[a.txt, mode:100644, content:other content]",
  560. indexState(CONTENT));
  561. }
  562. }
  563. @Test
  564. public void testAddRemovedFile() throws Exception {
  565. File file = new File(db.getWorkTree(), "a.txt");
  566. FileUtils.createNewFile(file);
  567. PrintWriter writer = new PrintWriter(file);
  568. writer.print("content");
  569. writer.close();
  570. try (Git git = new Git(db)) {
  571. DirCache dc = git.add().addFilepattern("a.txt").call();
  572. dc.getEntry(0).getObjectId();
  573. FileUtils.delete(file);
  574. // is supposed to do nothing
  575. dc = git.add().addFilepattern("a.txt").call();
  576. assertEquals(
  577. "[a.txt, mode:100644, content:content]",
  578. indexState(CONTENT));
  579. }
  580. }
  581. @Test
  582. public void testAddRemovedCommittedFile() throws Exception {
  583. File file = new File(db.getWorkTree(), "a.txt");
  584. FileUtils.createNewFile(file);
  585. PrintWriter writer = new PrintWriter(file);
  586. writer.print("content");
  587. writer.close();
  588. try (Git git = new Git(db)) {
  589. DirCache dc = git.add().addFilepattern("a.txt").call();
  590. git.commit().setMessage("commit a.txt").call();
  591. dc.getEntry(0).getObjectId();
  592. FileUtils.delete(file);
  593. // is supposed to do nothing
  594. dc = git.add().addFilepattern("a.txt").call();
  595. assertEquals(
  596. "[a.txt, mode:100644, content:content]",
  597. indexState(CONTENT));
  598. }
  599. }
  600. @Test
  601. public void testAddWithConflicts() throws Exception {
  602. // prepare conflict
  603. File file = new File(db.getWorkTree(), "a.txt");
  604. FileUtils.createNewFile(file);
  605. PrintWriter writer = new PrintWriter(file);
  606. writer.print("content");
  607. writer.close();
  608. File file2 = new File(db.getWorkTree(), "b.txt");
  609. FileUtils.createNewFile(file2);
  610. writer = new PrintWriter(file2);
  611. writer.print("content b");
  612. writer.close();
  613. ObjectInserter newObjectInserter = db.newObjectInserter();
  614. DirCache dc = db.lockDirCache();
  615. DirCacheBuilder builder = dc.builder();
  616. addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
  617. addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
  618. writer = new PrintWriter(file);
  619. writer.print("other content");
  620. writer.close();
  621. addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
  622. writer = new PrintWriter(file);
  623. writer.print("our content");
  624. writer.close();
  625. addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
  626. .getObjectId();
  627. builder.commit();
  628. assertEquals(
  629. "[a.txt, mode:100644, stage:1, content:content]" +
  630. "[a.txt, mode:100644, stage:2, content:our content]" +
  631. "[a.txt, mode:100644, stage:3, content:other content]" +
  632. "[b.txt, mode:100644, content:content b]",
  633. indexState(CONTENT));
  634. // now the test begins
  635. try (Git git = new Git(db)) {
  636. dc = git.add().addFilepattern("a.txt").call();
  637. assertEquals(
  638. "[a.txt, mode:100644, content:our content]" +
  639. "[b.txt, mode:100644, content:content b]",
  640. indexState(CONTENT));
  641. }
  642. }
  643. @Test
  644. public void testAddTwoFiles() throws Exception {
  645. File file = new File(db.getWorkTree(), "a.txt");
  646. FileUtils.createNewFile(file);
  647. PrintWriter writer = new PrintWriter(file);
  648. writer.print("content");
  649. writer.close();
  650. File file2 = new File(db.getWorkTree(), "b.txt");
  651. FileUtils.createNewFile(file2);
  652. writer = new PrintWriter(file2);
  653. writer.print("content b");
  654. writer.close();
  655. try (Git git = new Git(db)) {
  656. git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
  657. assertEquals(
  658. "[a.txt, mode:100644, content:content]" +
  659. "[b.txt, mode:100644, content:content b]",
  660. indexState(CONTENT));
  661. }
  662. }
  663. @Test
  664. public void testAddFolder() throws Exception {
  665. FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
  666. File file = new File(db.getWorkTree(), "sub/a.txt");
  667. FileUtils.createNewFile(file);
  668. PrintWriter writer = new PrintWriter(file);
  669. writer.print("content");
  670. writer.close();
  671. File file2 = new File(db.getWorkTree(), "sub/b.txt");
  672. FileUtils.createNewFile(file2);
  673. writer = new PrintWriter(file2);
  674. writer.print("content b");
  675. writer.close();
  676. try (Git git = new Git(db)) {
  677. git.add().addFilepattern("sub").call();
  678. assertEquals(
  679. "[sub/a.txt, mode:100644, content:content]" +
  680. "[sub/b.txt, mode:100644, content:content b]",
  681. indexState(CONTENT));
  682. }
  683. }
  684. @Test
  685. public void testAddIgnoredFile() throws Exception {
  686. FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
  687. File file = new File(db.getWorkTree(), "sub/a.txt");
  688. FileUtils.createNewFile(file);
  689. PrintWriter writer = new PrintWriter(file);
  690. writer.print("content");
  691. writer.close();
  692. File ignoreFile = new File(db.getWorkTree(), ".gitignore");
  693. FileUtils.createNewFile(ignoreFile);
  694. writer = new PrintWriter(ignoreFile);
  695. writer.print("sub/b.txt");
  696. writer.close();
  697. File file2 = new File(db.getWorkTree(), "sub/b.txt");
  698. FileUtils.createNewFile(file2);
  699. writer = new PrintWriter(file2);
  700. writer.print("content b");
  701. writer.close();
  702. try (Git git = new Git(db)) {
  703. git.add().addFilepattern("sub").call();
  704. assertEquals(
  705. "[sub/a.txt, mode:100644, content:content]",
  706. indexState(CONTENT));
  707. }
  708. }
  709. @Test
  710. public void testAddWholeRepo() throws Exception {
  711. FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
  712. File file = new File(db.getWorkTree(), "sub/a.txt");
  713. FileUtils.createNewFile(file);
  714. PrintWriter writer = new PrintWriter(file);
  715. writer.print("content");
  716. writer.close();
  717. File file2 = new File(db.getWorkTree(), "sub/b.txt");
  718. FileUtils.createNewFile(file2);
  719. writer = new PrintWriter(file2);
  720. writer.print("content b");
  721. writer.close();
  722. try (Git git = new Git(db)) {
  723. git.add().addFilepattern(".").call();
  724. assertEquals(
  725. "[sub/a.txt, mode:100644, content:content]" +
  726. "[sub/b.txt, mode:100644, content:content b]",
  727. indexState(CONTENT));
  728. }
  729. }
  730. // the same three cases as in testAddWithParameterUpdate
  731. // file a exists in workdir and in index -> added
  732. // file b exists not in workdir but in index -> unchanged
  733. // file c exists in workdir but not in index -> added
  734. @Test
  735. public void testAddWithoutParameterUpdate() throws Exception {
  736. FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
  737. File file = new File(db.getWorkTree(), "sub/a.txt");
  738. FileUtils.createNewFile(file);
  739. PrintWriter writer = new PrintWriter(file);
  740. writer.print("content");
  741. writer.close();
  742. File file2 = new File(db.getWorkTree(), "sub/b.txt");
  743. FileUtils.createNewFile(file2);
  744. writer = new PrintWriter(file2);
  745. writer.print("content b");
  746. writer.close();
  747. try (Git git = new Git(db)) {
  748. git.add().addFilepattern("sub").call();
  749. assertEquals(
  750. "[sub/a.txt, mode:100644, content:content]" +
  751. "[sub/b.txt, mode:100644, content:content b]",
  752. indexState(CONTENT));
  753. git.commit().setMessage("commit").call();
  754. // new unstaged file sub/c.txt
  755. File file3 = new File(db.getWorkTree(), "sub/c.txt");
  756. FileUtils.createNewFile(file3);
  757. writer = new PrintWriter(file3);
  758. writer.print("content c");
  759. writer.close();
  760. // file sub/a.txt is modified
  761. writer = new PrintWriter(file);
  762. writer.print("modified content");
  763. writer.close();
  764. // file sub/b.txt is deleted
  765. FileUtils.delete(file2);
  766. git.add().addFilepattern("sub").call();
  767. // change in sub/a.txt is staged
  768. // deletion of sub/b.txt is not staged
  769. // sub/c.txt is staged
  770. assertEquals(
  771. "[sub/a.txt, mode:100644, content:modified content]" +
  772. "[sub/b.txt, mode:100644, content:content b]" +
  773. "[sub/c.txt, mode:100644, content:content c]",
  774. indexState(CONTENT));
  775. }
  776. }
  777. // file a exists in workdir and in index -> added
  778. // file b exists not in workdir but in index -> deleted
  779. // file c exists in workdir but not in index -> unchanged
  780. @Test
  781. public void testAddWithParameterUpdate() throws Exception {
  782. FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
  783. File file = new File(db.getWorkTree(), "sub/a.txt");
  784. FileUtils.createNewFile(file);
  785. PrintWriter writer = new PrintWriter(file);
  786. writer.print("content");
  787. writer.close();
  788. File file2 = new File(db.getWorkTree(), "sub/b.txt");
  789. FileUtils.createNewFile(file2);
  790. writer = new PrintWriter(file2);
  791. writer.print("content b");
  792. writer.close();
  793. try (Git git = new Git(db)) {
  794. git.add().addFilepattern("sub").call();
  795. assertEquals(
  796. "[sub/a.txt, mode:100644, content:content]" +
  797. "[sub/b.txt, mode:100644, content:content b]",
  798. indexState(CONTENT));
  799. git.commit().setMessage("commit").call();
  800. // new unstaged file sub/c.txt
  801. File file3 = new File(db.getWorkTree(), "sub/c.txt");
  802. FileUtils.createNewFile(file3);
  803. writer = new PrintWriter(file3);
  804. writer.print("content c");
  805. writer.close();
  806. // file sub/a.txt is modified
  807. writer = new PrintWriter(file);
  808. writer.print("modified content");
  809. writer.close();
  810. FileUtils.delete(file2);
  811. // change in sub/a.txt is staged
  812. // deletion of sub/b.txt is staged
  813. // sub/c.txt is not staged
  814. git.add().addFilepattern("sub").setUpdate(true).call();
  815. // change in sub/a.txt is staged
  816. assertEquals(
  817. "[sub/a.txt, mode:100644, content:modified content]",
  818. indexState(CONTENT));
  819. }
  820. }
  821. @Test
  822. public void testAssumeUnchanged() throws Exception {
  823. try (Git git = new Git(db)) {
  824. String path = "a.txt";
  825. writeTrashFile(path, "content");
  826. git.add().addFilepattern(path).call();
  827. String path2 = "b.txt";
  828. writeTrashFile(path2, "content");
  829. git.add().addFilepattern(path2).call();
  830. git.commit().setMessage("commit").call();
  831. assertEquals("[a.txt, mode:100644, content:"
  832. + "content, assume-unchanged:false]"
  833. + "[b.txt, mode:100644, content:content, "
  834. + "assume-unchanged:false]", indexState(CONTENT
  835. | ASSUME_UNCHANGED));
  836. assumeUnchanged(path2);
  837. assertEquals("[a.txt, mode:100644, content:content, "
  838. + "assume-unchanged:false][b.txt, mode:100644, "
  839. + "content:content, assume-unchanged:true]", indexState(CONTENT
  840. | ASSUME_UNCHANGED));
  841. writeTrashFile(path, "more content");
  842. writeTrashFile(path2, "more content");
  843. git.add().addFilepattern(".").call();
  844. assertEquals("[a.txt, mode:100644, content:more content,"
  845. + " assume-unchanged:false][b.txt, mode:100644,"
  846. + " content:content, assume-unchanged:true]",
  847. indexState(CONTENT
  848. | ASSUME_UNCHANGED));
  849. }
  850. }
  851. @Test
  852. public void testReplaceFileWithDirectory()
  853. throws IOException, NoFilepatternException, GitAPIException {
  854. try (Git git = new Git(db)) {
  855. writeTrashFile("df", "before replacement");
  856. git.add().addFilepattern("df").call();
  857. assertEquals("[df, mode:100644, content:before replacement]",
  858. indexState(CONTENT));
  859. FileUtils.delete(new File(db.getWorkTree(), "df"));
  860. writeTrashFile("df/f", "after replacement");
  861. git.add().addFilepattern("df").call();
  862. assertEquals("[df/f, mode:100644, content:after replacement]",
  863. indexState(CONTENT));
  864. }
  865. }
  866. @Test
  867. public void testReplaceDirectoryWithFile()
  868. throws IOException, NoFilepatternException, GitAPIException {
  869. try (Git git = new Git(db)) {
  870. writeTrashFile("df/f", "before replacement");
  871. git.add().addFilepattern("df").call();
  872. assertEquals("[df/f, mode:100644, content:before replacement]",
  873. indexState(CONTENT));
  874. FileUtils.delete(new File(db.getWorkTree(), "df"), RECURSIVE);
  875. writeTrashFile("df", "after replacement");
  876. git.add().addFilepattern("df").call();
  877. assertEquals("[df, mode:100644, content:after replacement]",
  878. indexState(CONTENT));
  879. }
  880. }
  881. @Test
  882. public void testReplaceFileByPartOfDirectory()
  883. throws IOException, NoFilepatternException, GitAPIException {
  884. try (Git git = new Git(db)) {
  885. writeTrashFile("src/main", "df", "before replacement");
  886. writeTrashFile("src/main", "z", "z");
  887. writeTrashFile("z", "z2");
  888. git.add().addFilepattern("src/main/df")
  889. .addFilepattern("src/main/z")
  890. .addFilepattern("z")
  891. .call();
  892. assertEquals(
  893. "[src/main/df, mode:100644, content:before replacement]" +
  894. "[src/main/z, mode:100644, content:z]" +
  895. "[z, mode:100644, content:z2]",
  896. indexState(CONTENT));
  897. FileUtils.delete(new File(db.getWorkTree(), "src/main/df"));
  898. writeTrashFile("src/main/df", "a", "after replacement");
  899. writeTrashFile("src/main/df", "b", "unrelated file");
  900. git.add().addFilepattern("src/main/df/a").call();
  901. assertEquals(
  902. "[src/main/df/a, mode:100644, content:after replacement]" +
  903. "[src/main/z, mode:100644, content:z]" +
  904. "[z, mode:100644, content:z2]",
  905. indexState(CONTENT));
  906. }
  907. }
  908. @Test
  909. public void testReplaceDirectoryConflictsWithFile()
  910. throws IOException, NoFilepatternException, GitAPIException {
  911. DirCache dc = db.lockDirCache();
  912. try (ObjectInserter oi = db.newObjectInserter()) {
  913. DirCacheBuilder builder = dc.builder();
  914. File f = writeTrashFile("a", "df", "content");
  915. addEntryToBuilder("a", f, oi, builder, 1);
  916. f = writeTrashFile("a", "df", "other content");
  917. addEntryToBuilder("a/df", f, oi, builder, 3);
  918. f = writeTrashFile("a", "df", "our content");
  919. addEntryToBuilder("a/df", f, oi, builder, 2);
  920. f = writeTrashFile("z", "z");
  921. addEntryToBuilder("z", f, oi, builder, 0);
  922. builder.commit();
  923. }
  924. assertEquals(
  925. "[a, mode:100644, stage:1, content:content]" +
  926. "[a/df, mode:100644, stage:2, content:our content]" +
  927. "[a/df, mode:100644, stage:3, content:other content]" +
  928. "[z, mode:100644, content:z]",
  929. indexState(CONTENT));
  930. try (Git git = new Git(db)) {
  931. FileUtils.delete(new File(db.getWorkTree(), "a"), RECURSIVE);
  932. writeTrashFile("a", "merged");
  933. git.add().addFilepattern("a").call();
  934. assertEquals("[a, mode:100644, content:merged]" +
  935. "[z, mode:100644, content:z]",
  936. indexState(CONTENT));
  937. }
  938. }
  939. @Test
  940. public void testExecutableRetention() throws Exception {
  941. StoredConfig config = db.getConfig();
  942. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  943. ConfigConstants.CONFIG_KEY_FILEMODE, true);
  944. config.save();
  945. FS executableFs = new FS() {
  946. @Override
  947. public boolean supportsExecute() {
  948. return true;
  949. }
  950. @Override
  951. public boolean setExecute(File f, boolean canExec) {
  952. return true;
  953. }
  954. @Override
  955. public ProcessBuilder runInShell(String cmd, String[] args) {
  956. return null;
  957. }
  958. @Override
  959. public boolean retryFailedLockFileCommit() {
  960. return false;
  961. }
  962. @Override
  963. public FS newInstance() {
  964. return this;
  965. }
  966. @Override
  967. protected File discoverGitExe() {
  968. return null;
  969. }
  970. @Override
  971. public boolean canExecute(File f) {
  972. try {
  973. return read(f).startsWith("binary:");
  974. } catch (IOException e) {
  975. return false;
  976. }
  977. }
  978. @Override
  979. public boolean isCaseSensitive() {
  980. return false;
  981. }
  982. };
  983. Git git = Git.open(db.getDirectory(), executableFs);
  984. String path = "a.txt";
  985. String path2 = "a.sh";
  986. writeTrashFile(path, "content");
  987. writeTrashFile(path2, "binary: content");
  988. git.add().addFilepattern(path).addFilepattern(path2).call();
  989. RevCommit commit1 = git.commit().setMessage("commit").call();
  990. try (TreeWalk walk = new TreeWalk(db)) {
  991. walk.addTree(commit1.getTree());
  992. walk.next();
  993. assertEquals(path2, walk.getPathString());
  994. assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
  995. walk.next();
  996. assertEquals(path, walk.getPathString());
  997. assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
  998. }
  999. config = db.getConfig();
  1000. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  1001. ConfigConstants.CONFIG_KEY_FILEMODE, false);
  1002. config.save();
  1003. Git git2 = Git.open(db.getDirectory(), executableFs);
  1004. writeTrashFile(path2, "content2");
  1005. writeTrashFile(path, "binary: content2");
  1006. git2.add().addFilepattern(path).addFilepattern(path2).call();
  1007. RevCommit commit2 = git2.commit().setMessage("commit2").call();
  1008. try (TreeWalk walk = new TreeWalk(db)) {
  1009. walk.addTree(commit2.getTree());
  1010. walk.next();
  1011. assertEquals(path2, walk.getPathString());
  1012. assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
  1013. walk.next();
  1014. assertEquals(path, walk.getPathString());
  1015. assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
  1016. }
  1017. }
  1018. @Test
  1019. public void testAddGitlink() throws Exception {
  1020. createNestedRepo("git-link-dir");
  1021. try (Git git = new Git(db)) {
  1022. git.add().addFilepattern("git-link-dir").call();
  1023. assertEquals(
  1024. "[git-link-dir, mode:160000]",
  1025. indexState(0));
  1026. Set<String> untrackedFiles = git.status().call().getUntracked();
  1027. assert (untrackedFiles.isEmpty());
  1028. }
  1029. }
  1030. @Test
  1031. public void testAddSubrepoWithDirNoGitlinks() throws Exception {
  1032. createNestedRepo("nested-repo");
  1033. // Set DIR_NO_GITLINKS
  1034. StoredConfig config = db.getConfig();
  1035. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  1036. ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
  1037. config.save();
  1038. assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
  1039. try (Git git = new Git(db)) {
  1040. git.add().addFilepattern("nested-repo").call();
  1041. assertEquals(
  1042. "[nested-repo/README1.md, mode:100644]" +
  1043. "[nested-repo/README2.md, mode:100644]",
  1044. indexState(0));
  1045. }
  1046. // Turn off DIR_NO_GITLINKS, ensure nested-repo is still treated as
  1047. // a normal directory
  1048. // Set DIR_NO_GITLINKS
  1049. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  1050. ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, false);
  1051. config.save();
  1052. writeTrashFile("nested-repo", "README3.md", "content");
  1053. try (Git git = new Git(db)) {
  1054. git.add().addFilepattern("nested-repo").call();
  1055. assertEquals(
  1056. "[nested-repo/README1.md, mode:100644]" +
  1057. "[nested-repo/README2.md, mode:100644]" +
  1058. "[nested-repo/README3.md, mode:100644]",
  1059. indexState(0));
  1060. }
  1061. }
  1062. @Test
  1063. public void testAddGitlinkDoesNotChange() throws Exception {
  1064. createNestedRepo("nested-repo");
  1065. try (Git git = new Git(db)) {
  1066. git.add().addFilepattern("nested-repo").call();
  1067. assertEquals(
  1068. "[nested-repo, mode:160000]",
  1069. indexState(0));
  1070. }
  1071. // Set DIR_NO_GITLINKS
  1072. StoredConfig config = db.getConfig();
  1073. config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  1074. ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
  1075. config.save();
  1076. assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
  1077. try (Git git = new Git(db)) {
  1078. git.add().addFilepattern("nested-repo").call();
  1079. assertEquals(
  1080. "[nested-repo, mode:160000]",
  1081. indexState(0));
  1082. }
  1083. }
  1084. private static DirCacheEntry addEntryToBuilder(String path, File file,
  1085. ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage)
  1086. throws IOException {
  1087. FileInputStream inputStream = new FileInputStream(file);
  1088. ObjectId id = newObjectInserter.insert(
  1089. Constants.OBJ_BLOB, file.length(), inputStream);
  1090. inputStream.close();
  1091. DirCacheEntry entry = new DirCacheEntry(path, stage);
  1092. entry.setObjectId(id);
  1093. entry.setFileMode(FileMode.REGULAR_FILE);
  1094. entry.setLastModified(file.lastModified());
  1095. entry.setLength((int) file.length());
  1096. builder.add(entry);
  1097. return entry;
  1098. }
  1099. private void assumeUnchanged(String path) throws IOException {
  1100. final DirCache dirc = db.lockDirCache();
  1101. final DirCacheEntry ent = dirc.getEntry(path);
  1102. if (ent != null)
  1103. ent.setAssumeValid(true);
  1104. dirc.write();
  1105. if (!dirc.commit())
  1106. throw new IOException("could not commit");
  1107. }
  1108. private void createNestedRepo(String path) throws IOException {
  1109. File gitLinkDir = new File(db.getWorkTree(), path);
  1110. FileUtils.mkdir(gitLinkDir);
  1111. FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
  1112. nestedBuilder.setWorkTree(gitLinkDir);
  1113. Repository nestedRepo = nestedBuilder.build();
  1114. nestedRepo.create();
  1115. writeTrashFile(path, "README1.md", "content");
  1116. writeTrashFile(path, "README2.md", "content");
  1117. // Commit these changes in the subrepo
  1118. try (Git git = new Git(nestedRepo)) {
  1119. git.add().addFilepattern(".").call();
  1120. git.commit().setMessage("subrepo commit").call();
  1121. } catch (GitAPIException e) {
  1122. throw new RuntimeException(e);
  1123. }
  1124. }
  1125. }