You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RefUpdateTest.java 36KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. /*
  2. * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
  3. * Copyright (C) 2009-2010, Google Inc.
  4. * Copyright (C) 2008-2013, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * and other copyright owners as documented in the project's IP log.
  6. *
  7. * This program and the accompanying materials are made available
  8. * under the terms of the Eclipse Distribution License v1.0 which
  9. * accompanies this distribution, is reproduced below, and is
  10. * available at http://www.eclipse.org/org/documents/edl-v10.php
  11. *
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or
  15. * without modification, are permitted provided that the following
  16. * conditions are met:
  17. *
  18. * - Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. *
  21. * - Redistributions in binary form must reproduce the above
  22. * copyright notice, this list of conditions and the following
  23. * disclaimer in the documentation and/or other materials provided
  24. * with the distribution.
  25. *
  26. * - Neither the name of the Eclipse Foundation, Inc. nor the
  27. * names of its contributors may be used to endorse or promote
  28. * products derived from this software without specific prior
  29. * written permission.
  30. *
  31. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  34. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  36. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  41. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  42. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  43. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. */
  45. package org.eclipse.jgit.internal.storage.file;
  46. import static org.eclipse.jgit.junit.Assert.assertEquals;
  47. import static org.eclipse.jgit.lib.Constants.CHARSET;
  48. import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
  49. import static org.junit.Assert.assertEquals;
  50. import static org.junit.Assert.assertFalse;
  51. import static org.junit.Assert.assertNotNull;
  52. import static org.junit.Assert.assertNotSame;
  53. import static org.junit.Assert.assertNull;
  54. import static org.junit.Assert.assertSame;
  55. import static org.junit.Assert.assertTrue;
  56. import static org.junit.Assert.fail;
  57. import java.io.File;
  58. import java.io.IOException;
  59. import java.util.List;
  60. import java.util.Map;
  61. import java.util.Map.Entry;
  62. import org.eclipse.jgit.lib.AnyObjectId;
  63. import org.eclipse.jgit.lib.Constants;
  64. import org.eclipse.jgit.lib.ObjectId;
  65. import org.eclipse.jgit.lib.ObjectInserter;
  66. import org.eclipse.jgit.lib.PersonIdent;
  67. import org.eclipse.jgit.lib.Ref;
  68. import org.eclipse.jgit.lib.RefRename;
  69. import org.eclipse.jgit.lib.RefUpdate;
  70. import org.eclipse.jgit.lib.RefUpdate.Result;
  71. import org.eclipse.jgit.lib.ReflogEntry;
  72. import org.eclipse.jgit.lib.ReflogReader;
  73. import org.eclipse.jgit.lib.Repository;
  74. import org.eclipse.jgit.revwalk.RevCommit;
  75. import org.eclipse.jgit.revwalk.RevWalk;
  76. import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
  77. import org.junit.Test;
  78. public class RefUpdateTest extends SampleDataRepositoryTestCase {
  79. private void writeSymref(String src, String dst) throws IOException {
  80. RefUpdate u = db.updateRef(src);
  81. switch (u.link(dst)) {
  82. case NEW:
  83. case FORCED:
  84. case NO_CHANGE:
  85. break;
  86. default:
  87. fail("link " + src + " to " + dst);
  88. }
  89. }
  90. private RefUpdate updateRef(String name) throws IOException {
  91. final RefUpdate ref = db.updateRef(name);
  92. ref.setNewObjectId(db.resolve(Constants.HEAD));
  93. return ref;
  94. }
  95. private void delete(RefUpdate ref, Result expected)
  96. throws IOException {
  97. delete(ref, expected, true, true);
  98. }
  99. private void delete(final RefUpdate ref, final Result expected,
  100. final boolean exists, final boolean removed) throws IOException {
  101. delete(db, ref, expected, exists, removed);
  102. }
  103. private void delete(Repository repo, final RefUpdate ref, final Result expected,
  104. final boolean exists, final boolean removed) throws IOException {
  105. assertEquals(exists, repo.getAllRefs().containsKey(ref.getName()));
  106. assertEquals(expected, ref.delete());
  107. assertEquals(!removed, repo.getAllRefs().containsKey(ref.getName()));
  108. }
  109. @Test
  110. public void testNoCacheObjectIdSubclass() throws IOException {
  111. final String newRef = "refs/heads/abc";
  112. final RefUpdate ru = updateRef(newRef);
  113. final SubclassedId newid = new SubclassedId(ru.getNewObjectId());
  114. ru.setNewObjectId(newid);
  115. Result update = ru.update();
  116. assertEquals(Result.NEW, update);
  117. final Ref r = db.getAllRefs().get(newRef);
  118. assertNotNull(r);
  119. assertEquals(newRef, r.getName());
  120. assertNotNull(r.getObjectId());
  121. assertNotSame(newid, r.getObjectId());
  122. assertSame(ObjectId.class, r.getObjectId().getClass());
  123. assertEquals(newid, r.getObjectId());
  124. List<ReflogEntry> reverseEntries1 = db
  125. .getReflogReader("refs/heads/abc").getReverseEntries();
  126. ReflogEntry entry1 = reverseEntries1.get(0);
  127. assertEquals(1, reverseEntries1.size());
  128. assertEquals(ObjectId.zeroId(), entry1.getOldId());
  129. assertEquals(r.getObjectId(), entry1.getNewId());
  130. assertEquals(new PersonIdent(db).toString(), entry1.getWho().toString());
  131. assertEquals("", entry1.getComment());
  132. List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
  133. .getReverseEntries();
  134. assertEquals(0, reverseEntries2.size());
  135. }
  136. @Test
  137. public void testNewNamespaceConflictWithLoosePrefixNameExists()
  138. throws IOException {
  139. final String newRef = "refs/heads/z";
  140. final RefUpdate ru = updateRef(newRef);
  141. Result update = ru.update();
  142. assertEquals(Result.NEW, update);
  143. // end setup
  144. final String newRef2 = "refs/heads/z/a";
  145. final RefUpdate ru2 = updateRef(newRef2);
  146. Result update2 = ru2.update();
  147. assertEquals(Result.LOCK_FAILURE, update2);
  148. assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size());
  149. assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
  150. }
  151. @Test
  152. public void testNewNamespaceConflictWithPackedPrefixNameExists()
  153. throws IOException {
  154. final String newRef = "refs/heads/master/x";
  155. final RefUpdate ru = updateRef(newRef);
  156. Result update = ru.update();
  157. assertEquals(Result.LOCK_FAILURE, update);
  158. assertNull(db.getReflogReader("refs/heads/master/x"));
  159. assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
  160. }
  161. @Test
  162. public void testNewNamespaceConflictWithLoosePrefixOfExisting()
  163. throws IOException {
  164. final String newRef = "refs/heads/z/a";
  165. final RefUpdate ru = updateRef(newRef);
  166. Result update = ru.update();
  167. assertEquals(Result.NEW, update);
  168. // end setup
  169. final String newRef2 = "refs/heads/z";
  170. final RefUpdate ru2 = updateRef(newRef2);
  171. Result update2 = ru2.update();
  172. assertEquals(Result.LOCK_FAILURE, update2);
  173. assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size());
  174. assertNull(db.getReflogReader("refs/heads/z"));
  175. assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
  176. }
  177. @Test
  178. public void testNewNamespaceConflictWithPackedPrefixOfExisting()
  179. throws IOException {
  180. final String newRef = "refs/heads/prefix";
  181. final RefUpdate ru = updateRef(newRef);
  182. Result update = ru.update();
  183. assertEquals(Result.LOCK_FAILURE, update);
  184. assertNull(db.getReflogReader("refs/heads/prefix"));
  185. assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
  186. }
  187. /**
  188. * Delete a ref that is pointed to by HEAD
  189. *
  190. * @throws IOException
  191. */
  192. @Test
  193. public void testDeleteHEADreferencedRef() throws IOException {
  194. ObjectId pid = db.resolve("refs/heads/master^");
  195. RefUpdate updateRef = db.updateRef("refs/heads/master");
  196. updateRef.setNewObjectId(pid);
  197. updateRef.setForceUpdate(true);
  198. Result update = updateRef.update();
  199. assertEquals(Result.FORCED, update); // internal
  200. RefUpdate updateRef2 = db.updateRef("refs/heads/master");
  201. Result delete = updateRef2.delete();
  202. assertEquals(Result.REJECTED_CURRENT_BRANCH, delete);
  203. assertEquals(pid, db.resolve("refs/heads/master"));
  204. assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
  205. assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size());
  206. }
  207. @Test
  208. public void testLooseDelete() throws IOException {
  209. final String newRef = "refs/heads/abc";
  210. RefUpdate ref = updateRef(newRef);
  211. ref.update(); // create loose ref
  212. ref = updateRef(newRef); // refresh
  213. delete(ref, Result.NO_CHANGE);
  214. assertNull(db.getReflogReader("refs/heads/abc"));
  215. }
  216. @Test
  217. public void testDeleteHead() throws IOException {
  218. final RefUpdate ref = updateRef(Constants.HEAD);
  219. delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false);
  220. assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size());
  221. assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
  222. }
  223. @Test
  224. public void testDeleteHeadInBareRepo() throws IOException {
  225. Repository bareRepo = createBareRepository();
  226. String master = "refs/heads/master";
  227. Ref head = bareRepo.exactRef(Constants.HEAD);
  228. assertNotNull(head);
  229. assertTrue(head.isSymbolic());
  230. assertEquals(master, head.getLeaf().getName());
  231. assertNull(head.getObjectId());
  232. assertNull(bareRepo.exactRef(master));
  233. ObjectId blobId;
  234. try (ObjectInserter ins = bareRepo.newObjectInserter()) {
  235. blobId = ins.insert(Constants.OBJ_BLOB, "contents".getBytes(CHARSET));
  236. ins.flush();
  237. }
  238. // Create master via HEAD, so we delete it.
  239. RefUpdate ref = bareRepo.updateRef(Constants.HEAD);
  240. ref.setNewObjectId(blobId);
  241. assertEquals(Result.NEW, ref.update());
  242. head = bareRepo.exactRef(Constants.HEAD);
  243. assertTrue(head.isSymbolic());
  244. assertEquals(master, head.getLeaf().getName());
  245. assertEquals(blobId, head.getLeaf().getObjectId());
  246. assertEquals(blobId, bareRepo.exactRef(master).getObjectId());
  247. // Unlike in a non-bare repo, deleting the HEAD is allowed, and leaves HEAD
  248. // back in a dangling state.
  249. ref = bareRepo.updateRef(Constants.HEAD);
  250. ref.setExpectedOldObjectId(blobId);
  251. ref.setForceUpdate(true);
  252. delete(bareRepo, ref, Result.FORCED, true, true);
  253. head = bareRepo.exactRef(Constants.HEAD);
  254. assertNotNull(head);
  255. assertTrue(head.isSymbolic());
  256. assertEquals(master, head.getLeaf().getName());
  257. assertNull(head.getObjectId());
  258. assertNull(bareRepo.exactRef(master));
  259. }
  260. @Test
  261. public void testDeleteSymref() throws IOException {
  262. RefUpdate dst = updateRef("refs/heads/abc");
  263. assertEquals(Result.NEW, dst.update());
  264. ObjectId id = dst.getNewObjectId();
  265. RefUpdate u = db.updateRef("refs/symref");
  266. assertEquals(Result.NEW, u.link(dst.getName()));
  267. Ref ref = db.exactRef(u.getName());
  268. assertNotNull(ref);
  269. assertTrue(ref.isSymbolic());
  270. assertEquals(dst.getName(), ref.getLeaf().getName());
  271. assertEquals(id, ref.getLeaf().getObjectId());
  272. u = db.updateRef(u.getName());
  273. u.setDetachingSymbolicRef();
  274. u.setForceUpdate(true);
  275. assertEquals(Result.FORCED, u.delete());
  276. assertNull(db.exactRef(u.getName()));
  277. ref = db.exactRef(dst.getName());
  278. assertNotNull(ref);
  279. assertFalse(ref.isSymbolic());
  280. assertEquals(id, ref.getObjectId());
  281. }
  282. /**
  283. * Delete a loose ref and make sure the directory in refs is deleted too,
  284. * and the reflog dir too
  285. *
  286. * @throws IOException
  287. */
  288. @Test
  289. public void testDeleteLooseAndItsDirectory() throws IOException {
  290. ObjectId pid = db.resolve("refs/heads/c^");
  291. RefUpdate updateRef = db.updateRef("refs/heads/z/c");
  292. updateRef.setNewObjectId(pid);
  293. updateRef.setForceUpdate(true);
  294. updateRef.setRefLogMessage("new test ref", false);
  295. Result update = updateRef.update();
  296. assertEquals(Result.NEW, update); // internal
  297. assertTrue(new File(db.getDirectory(), Constants.R_HEADS + "z")
  298. .exists());
  299. assertTrue(new File(db.getDirectory(), "logs/refs/heads/z").exists());
  300. // The real test here
  301. RefUpdate updateRef2 = db.updateRef("refs/heads/z/c");
  302. updateRef2.setForceUpdate(true);
  303. Result delete = updateRef2.delete();
  304. assertEquals(Result.FORCED, delete);
  305. assertNull(db.resolve("refs/heads/z/c"));
  306. assertFalse(new File(db.getDirectory(), Constants.R_HEADS + "z")
  307. .exists());
  308. assertFalse(new File(db.getDirectory(), "logs/refs/heads/z").exists());
  309. }
  310. @Test
  311. public void testDeleteNotFound() throws IOException {
  312. final RefUpdate ref = updateRef("refs/heads/xyz");
  313. delete(ref, Result.NEW, false, true);
  314. }
  315. @Test
  316. public void testDeleteFastForward() throws IOException {
  317. final RefUpdate ref = updateRef("refs/heads/a");
  318. delete(ref, Result.FAST_FORWARD);
  319. }
  320. @Test
  321. public void testDeleteForce() throws IOException {
  322. final RefUpdate ref = db.updateRef("refs/heads/b");
  323. ref.setNewObjectId(db.resolve("refs/heads/a"));
  324. delete(ref, Result.REJECTED, true, false);
  325. ref.setForceUpdate(true);
  326. delete(ref, Result.FORCED);
  327. }
  328. @Test
  329. public void testDeleteWithoutHead() throws IOException {
  330. // Prepare repository without HEAD
  331. RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
  332. refUpdate.setForceUpdate(true);
  333. refUpdate.setNewObjectId(ObjectId.zeroId());
  334. Result updateResult = refUpdate.update();
  335. assertEquals(Result.FORCED, updateResult);
  336. Result deleteHeadResult = db.updateRef(Constants.HEAD).delete();
  337. assertEquals(Result.NO_CHANGE, deleteHeadResult);
  338. // Any result is ok as long as it's not an NPE
  339. db.updateRef(Constants.R_HEADS + "master").delete();
  340. }
  341. @Test
  342. public void testRefKeySameAsName() {
  343. Map<String, Ref> allRefs = db.getAllRefs();
  344. for (Entry<String, Ref> e : allRefs.entrySet()) {
  345. assertEquals(e.getKey(), e.getValue().getName());
  346. }
  347. }
  348. /**
  349. * Try modify a ref forward, fast forward
  350. *
  351. * @throws IOException
  352. */
  353. @Test
  354. public void testUpdateRefForward() throws IOException {
  355. ObjectId ppid = db.resolve("refs/heads/master^");
  356. ObjectId pid = db.resolve("refs/heads/master");
  357. RefUpdate updateRef = db.updateRef("refs/heads/master");
  358. updateRef.setNewObjectId(ppid);
  359. updateRef.setForceUpdate(true);
  360. Result update = updateRef.update();
  361. assertEquals(Result.FORCED, update);
  362. assertEquals(ppid, db.resolve("refs/heads/master"));
  363. // real test
  364. RefUpdate updateRef2 = db.updateRef("refs/heads/master");
  365. updateRef2.setNewObjectId(pid);
  366. Result update2 = updateRef2.update();
  367. assertEquals(Result.FAST_FORWARD, update2);
  368. assertEquals(pid, db.resolve("refs/heads/master"));
  369. }
  370. /**
  371. * Update the HEAD ref. Only it should be changed, not what it points to.
  372. *
  373. * @throws Exception
  374. */
  375. @Test
  376. public void testUpdateRefDetached() throws Exception {
  377. ObjectId pid = db.resolve("refs/heads/master");
  378. ObjectId ppid = db.resolve("refs/heads/master^");
  379. RefUpdate updateRef = db.updateRef("HEAD", true);
  380. updateRef.setForceUpdate(true);
  381. updateRef.setNewObjectId(ppid);
  382. Result update = updateRef.update();
  383. assertEquals(Result.FORCED, update);
  384. assertEquals(ppid, db.resolve("HEAD"));
  385. Ref ref = db.exactRef("HEAD");
  386. assertEquals("HEAD", ref.getName());
  387. assertTrue("is detached", !ref.isSymbolic());
  388. // the branch HEAD referred to is left untouched
  389. assertEquals(pid, db.resolve("refs/heads/master"));
  390. ReflogReader reflogReader = db.getReflogReader("HEAD");
  391. ReflogEntry e = reflogReader.getReverseEntries().get(0);
  392. assertEquals(pid, e.getOldId());
  393. assertEquals(ppid, e.getNewId());
  394. assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
  395. assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
  396. assertEquals(1250379778000L, e.getWho().getWhen().getTime());
  397. }
  398. /**
  399. * Update the HEAD ref when the referenced branch is unborn
  400. *
  401. * @throws Exception
  402. */
  403. @Test
  404. public void testUpdateRefDetachedUnbornHead() throws Exception {
  405. ObjectId ppid = db.resolve("refs/heads/master^");
  406. writeSymref("HEAD", "refs/heads/unborn");
  407. RefUpdate updateRef = db.updateRef("HEAD", true);
  408. updateRef.setForceUpdate(true);
  409. updateRef.setNewObjectId(ppid);
  410. Result update = updateRef.update();
  411. assertEquals(Result.NEW, update);
  412. assertEquals(ppid, db.resolve("HEAD"));
  413. Ref ref = db.exactRef("HEAD");
  414. assertEquals("HEAD", ref.getName());
  415. assertTrue("is detached", !ref.isSymbolic());
  416. // the branch HEAD referred to is left untouched
  417. assertNull(db.resolve("refs/heads/unborn"));
  418. ReflogReader reflogReader = db.getReflogReader("HEAD");
  419. ReflogEntry e = reflogReader.getReverseEntries().get(0);
  420. assertEquals(ObjectId.zeroId(), e.getOldId());
  421. assertEquals(ppid, e.getNewId());
  422. assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
  423. assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
  424. assertEquals(1250379778000L, e.getWho().getWhen().getTime());
  425. }
  426. /**
  427. * Delete a ref that exists both as packed and loose. Make sure the ref
  428. * cannot be resolved after delete.
  429. *
  430. * @throws IOException
  431. */
  432. @Test
  433. public void testDeleteLoosePacked() throws IOException {
  434. ObjectId pid = db.resolve("refs/heads/c^");
  435. RefUpdate updateRef = db.updateRef("refs/heads/c");
  436. updateRef.setNewObjectId(pid);
  437. updateRef.setForceUpdate(true);
  438. Result update = updateRef.update();
  439. assertEquals(Result.FORCED, update); // internal
  440. // The real test here
  441. RefUpdate updateRef2 = db.updateRef("refs/heads/c");
  442. updateRef2.setForceUpdate(true);
  443. Result delete = updateRef2.delete();
  444. assertEquals(Result.FORCED, delete);
  445. assertNull(db.resolve("refs/heads/c"));
  446. }
  447. /**
  448. * Try modify a ref to same
  449. *
  450. * @throws IOException
  451. */
  452. @Test
  453. public void testUpdateRefNoChange() throws IOException {
  454. ObjectId pid = db.resolve("refs/heads/master");
  455. RefUpdate updateRef = db.updateRef("refs/heads/master");
  456. updateRef.setNewObjectId(pid);
  457. Result update = updateRef.update();
  458. assertEquals(Result.NO_CHANGE, update);
  459. assertEquals(pid, db.resolve("refs/heads/master"));
  460. }
  461. /**
  462. * Test case originating from
  463. * <a href="http://bugs.eclipse.org/285991">bug 285991</a>
  464. *
  465. * Make sure the in memory cache is updated properly after
  466. * update of symref. This one did not fail because the
  467. * ref was packed due to implementation issues.
  468. *
  469. * @throws Exception
  470. */
  471. @Test
  472. public void testRefsCacheAfterUpdate() throws Exception {
  473. // Do not use the defalt repo for this case.
  474. Map<String, Ref> allRefs = db.getAllRefs();
  475. ObjectId oldValue = db.resolve("HEAD");
  476. ObjectId newValue = db.resolve("HEAD^");
  477. // first make HEAD refer to loose ref
  478. RefUpdate updateRef = db.updateRef(Constants.HEAD);
  479. updateRef.setForceUpdate(true);
  480. updateRef.setNewObjectId(newValue);
  481. Result update = updateRef.update();
  482. assertEquals(Result.FORCED, update);
  483. // now update that ref
  484. updateRef = db.updateRef(Constants.HEAD);
  485. updateRef.setNewObjectId(oldValue);
  486. update = updateRef.update();
  487. assertEquals(Result.FAST_FORWARD, update);
  488. allRefs = db.getAllRefs();
  489. Ref master = allRefs.get("refs/heads/master");
  490. Ref head = allRefs.get("HEAD");
  491. assertEquals("refs/heads/master", master.getName());
  492. assertEquals("HEAD", head.getName());
  493. assertTrue("is symbolic reference", head.isSymbolic());
  494. assertSame(master, head.getTarget());
  495. }
  496. /**
  497. * Test case originating from
  498. * <a href="http://bugs.eclipse.org/285991">bug 285991</a>
  499. *
  500. * Make sure the in memory cache is updated properly after
  501. * update of symref.
  502. *
  503. * @throws Exception
  504. */
  505. @Test
  506. public void testRefsCacheAfterUpdateLooseOnly() throws Exception {
  507. // Do not use the defalt repo for this case.
  508. Map<String, Ref> allRefs = db.getAllRefs();
  509. ObjectId oldValue = db.resolve("HEAD");
  510. writeSymref(Constants.HEAD, "refs/heads/newref");
  511. RefUpdate updateRef = db.updateRef(Constants.HEAD);
  512. updateRef.setForceUpdate(true);
  513. updateRef.setNewObjectId(oldValue);
  514. Result update = updateRef.update();
  515. assertEquals(Result.NEW, update);
  516. allRefs = db.getAllRefs();
  517. Ref head = allRefs.get("HEAD");
  518. Ref newref = allRefs.get("refs/heads/newref");
  519. assertEquals("refs/heads/newref", newref.getName());
  520. assertEquals("HEAD", head.getName());
  521. assertTrue("is symbolic reference", head.isSymbolic());
  522. assertSame(newref, head.getTarget());
  523. }
  524. /**
  525. * Try modify a ref, but get wrong expected old value
  526. *
  527. * @throws IOException
  528. */
  529. @Test
  530. public void testUpdateRefLockFailureWrongOldValue() throws IOException {
  531. ObjectId pid = db.resolve("refs/heads/master");
  532. RefUpdate updateRef = db.updateRef("refs/heads/master");
  533. updateRef.setNewObjectId(pid);
  534. updateRef.setExpectedOldObjectId(db.resolve("refs/heads/master^"));
  535. Result update = updateRef.update();
  536. assertEquals(Result.LOCK_FAILURE, update);
  537. assertEquals(pid, db.resolve("refs/heads/master"));
  538. }
  539. /**
  540. * Try modify a ref forward, fast forward, checking old value first
  541. *
  542. * @throws IOException
  543. */
  544. @Test
  545. public void testUpdateRefForwardWithCheck1() throws IOException {
  546. ObjectId ppid = db.resolve("refs/heads/master^");
  547. ObjectId pid = db.resolve("refs/heads/master");
  548. RefUpdate updateRef = db.updateRef("refs/heads/master");
  549. updateRef.setNewObjectId(ppid);
  550. updateRef.setForceUpdate(true);
  551. Result update = updateRef.update();
  552. assertEquals(Result.FORCED, update);
  553. assertEquals(ppid, db.resolve("refs/heads/master"));
  554. // real test
  555. RefUpdate updateRef2 = db.updateRef("refs/heads/master");
  556. updateRef2.setExpectedOldObjectId(ppid);
  557. updateRef2.setNewObjectId(pid);
  558. Result update2 = updateRef2.update();
  559. assertEquals(Result.FAST_FORWARD, update2);
  560. assertEquals(pid, db.resolve("refs/heads/master"));
  561. }
  562. /**
  563. * Try modify a ref forward, fast forward, checking old commit first
  564. *
  565. * @throws IOException
  566. */
  567. @Test
  568. public void testUpdateRefForwardWithCheck2() throws IOException {
  569. ObjectId ppid = db.resolve("refs/heads/master^");
  570. ObjectId pid = db.resolve("refs/heads/master");
  571. RefUpdate updateRef = db.updateRef("refs/heads/master");
  572. updateRef.setNewObjectId(ppid);
  573. updateRef.setForceUpdate(true);
  574. Result update = updateRef.update();
  575. assertEquals(Result.FORCED, update);
  576. assertEquals(ppid, db.resolve("refs/heads/master"));
  577. // real test
  578. try (RevWalk rw = new RevWalk(db)) {
  579. RevCommit old = rw.parseCommit(ppid);
  580. RefUpdate updateRef2 = db.updateRef("refs/heads/master");
  581. updateRef2.setExpectedOldObjectId(old);
  582. updateRef2.setNewObjectId(pid);
  583. Result update2 = updateRef2.update();
  584. assertEquals(Result.FAST_FORWARD, update2);
  585. assertEquals(pid, db.resolve("refs/heads/master"));
  586. }
  587. }
  588. /**
  589. * Try modify a ref that is locked
  590. *
  591. * @throws IOException
  592. */
  593. @Test
  594. public void testUpdateRefLockFailureLocked() throws IOException {
  595. ObjectId opid = db.resolve("refs/heads/master");
  596. ObjectId pid = db.resolve("refs/heads/master^");
  597. RefUpdate updateRef = db.updateRef("refs/heads/master");
  598. updateRef.setNewObjectId(pid);
  599. LockFile lockFile1 = new LockFile(new File(db.getDirectory(),
  600. "refs/heads/master"));
  601. try {
  602. assertTrue(lockFile1.lock()); // precondition to test
  603. Result update = updateRef.update();
  604. assertEquals(Result.LOCK_FAILURE, update);
  605. assertEquals(opid, db.resolve("refs/heads/master"));
  606. LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master"));
  607. assertFalse(lockFile2.lock()); // was locked, still is
  608. } finally {
  609. lockFile1.unlock();
  610. }
  611. }
  612. /**
  613. * Try to delete a ref. Delete requires force.
  614. *
  615. * @throws IOException
  616. */
  617. @Test
  618. public void testDeleteLoosePackedRejected() throws IOException {
  619. ObjectId pid = db.resolve("refs/heads/c^");
  620. ObjectId oldpid = db.resolve("refs/heads/c");
  621. RefUpdate updateRef = db.updateRef("refs/heads/c");
  622. updateRef.setNewObjectId(pid);
  623. Result update = updateRef.update();
  624. assertEquals(Result.REJECTED, update);
  625. assertEquals(oldpid, db.resolve("refs/heads/c"));
  626. }
  627. @Test
  628. public void testRenameBranchNoPreviousLog() throws IOException {
  629. assertFalse("precondition, no log on old branchg", new File(db
  630. .getDirectory(), "logs/refs/heads/b").exists());
  631. ObjectId rb = db.resolve("refs/heads/b");
  632. ObjectId oldHead = db.resolve(Constants.HEAD);
  633. assertFalse(rb.equals(oldHead)); // assumption for this test
  634. RefRename renameRef = db.renameRef("refs/heads/b",
  635. "refs/heads/new/name");
  636. Result result = renameRef.rename();
  637. assertEquals(Result.RENAMED, result);
  638. assertEquals(rb, db.resolve("refs/heads/new/name"));
  639. assertNull(db.resolve("refs/heads/b"));
  640. assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size());
  641. assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
  642. .getLastEntry().getComment());
  643. assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
  644. assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
  645. }
  646. @Test
  647. public void testRenameBranchHasPreviousLog() throws IOException {
  648. ObjectId rb = db.resolve("refs/heads/b");
  649. ObjectId oldHead = db.resolve(Constants.HEAD);
  650. assertFalse("precondition for this test, branch b != HEAD", rb
  651. .equals(oldHead));
  652. writeReflog(db, rb, "Just a message", "refs/heads/b");
  653. assertTrue("log on old branch", new File(db.getDirectory(),
  654. "logs/refs/heads/b").exists());
  655. RefRename renameRef = db.renameRef("refs/heads/b",
  656. "refs/heads/new/name");
  657. Result result = renameRef.rename();
  658. assertEquals(Result.RENAMED, result);
  659. assertEquals(rb, db.resolve("refs/heads/new/name"));
  660. assertNull(db.resolve("refs/heads/b"));
  661. assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
  662. assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
  663. .getLastEntry().getComment());
  664. assertEquals("Just a message", db.getReflogReader("new/name")
  665. .getReverseEntries().get(1).getComment());
  666. assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
  667. assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
  668. }
  669. @Test
  670. public void testRenameCurrentBranch() throws IOException {
  671. ObjectId rb = db.resolve("refs/heads/b");
  672. writeSymref(Constants.HEAD, "refs/heads/b");
  673. ObjectId oldHead = db.resolve(Constants.HEAD);
  674. assertEquals("internal test condition, b == HEAD", oldHead, rb);
  675. writeReflog(db, rb, "Just a message", "refs/heads/b");
  676. assertTrue("log on old branch", new File(db.getDirectory(),
  677. "logs/refs/heads/b").exists());
  678. RefRename renameRef = db.renameRef("refs/heads/b",
  679. "refs/heads/new/name");
  680. Result result = renameRef.rename();
  681. assertEquals(Result.RENAMED, result);
  682. assertEquals(rb, db.resolve("refs/heads/new/name"));
  683. assertNull(db.resolve("refs/heads/b"));
  684. assertEquals("Branch: renamed b to new/name", db.getReflogReader(
  685. "new/name").getLastEntry().getComment());
  686. assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
  687. assertEquals(rb, db.resolve(Constants.HEAD));
  688. assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
  689. assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment());
  690. assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment());
  691. }
  692. @Test
  693. public void testRenameBranchAlsoInPack() throws IOException {
  694. ObjectId rb = db.resolve("refs/heads/b");
  695. ObjectId rb2 = db.resolve("refs/heads/b~1");
  696. assertEquals(Ref.Storage.PACKED, db.exactRef("refs/heads/b").getStorage());
  697. RefUpdate updateRef = db.updateRef("refs/heads/b");
  698. updateRef.setNewObjectId(rb2);
  699. updateRef.setForceUpdate(true);
  700. Result update = updateRef.update();
  701. assertEquals("internal check new ref is loose", Result.FORCED, update);
  702. assertEquals(Ref.Storage.LOOSE, db.exactRef("refs/heads/b").getStorage());
  703. writeReflog(db, rb, "Just a message", "refs/heads/b");
  704. assertTrue("log on old branch", new File(db.getDirectory(),
  705. "logs/refs/heads/b").exists());
  706. RefRename renameRef = db.renameRef("refs/heads/b",
  707. "refs/heads/new/name");
  708. Result result = renameRef.rename();
  709. assertEquals(Result.RENAMED, result);
  710. assertEquals(rb2, db.resolve("refs/heads/new/name"));
  711. assertNull(db.resolve("refs/heads/b"));
  712. assertEquals("Branch: renamed b to new/name", db.getReflogReader(
  713. "new/name").getLastEntry().getComment());
  714. assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size());
  715. assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment());
  716. assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
  717. // make sure b's log file is gone too.
  718. assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
  719. // Create new Repository instance, to reread caches and make sure our
  720. // assumptions are persistent.
  721. try (Repository ndb = new FileRepository(db.getDirectory())) {
  722. assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
  723. assertNull(ndb.resolve("refs/heads/b"));
  724. }
  725. }
  726. public void tryRenameWhenLocked(String toLock, String fromName,
  727. String toName, String headPointsTo) throws IOException {
  728. // setup
  729. writeSymref(Constants.HEAD, headPointsTo);
  730. ObjectId oldfromId = db.resolve(fromName);
  731. ObjectId oldHeadId = db.resolve(Constants.HEAD);
  732. writeReflog(db, oldfromId, "Just a message", fromName);
  733. List<ReflogEntry> oldFromLog = db
  734. .getReflogReader(fromName).getReverseEntries();
  735. List<ReflogEntry> oldHeadLog = oldHeadId != null ? db
  736. .getReflogReader(Constants.HEAD).getReverseEntries() : null;
  737. assertTrue("internal check, we have a log", new File(db.getDirectory(),
  738. "logs/" + fromName).exists());
  739. // "someone" has branch X locked
  740. LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock));
  741. try {
  742. assertTrue(lockFile.lock());
  743. // Now this is our test
  744. RefRename renameRef = db.renameRef(fromName, toName);
  745. Result result = renameRef.rename();
  746. assertEquals(Result.LOCK_FAILURE, result);
  747. // Check that the involved refs are the same despite the failure
  748. assertExists(false, toName);
  749. if (!toLock.equals(toName))
  750. assertExists(false, toName + LOCK_SUFFIX);
  751. assertExists(true, toLock + LOCK_SUFFIX);
  752. if (!toLock.equals(fromName))
  753. assertExists(false, "logs/" + fromName + LOCK_SUFFIX);
  754. assertExists(false, "logs/" + toName + LOCK_SUFFIX);
  755. assertEquals(oldHeadId, db.resolve(Constants.HEAD));
  756. assertEquals(oldfromId, db.resolve(fromName));
  757. assertNull(db.resolve(toName));
  758. assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
  759. .getReverseEntries().toString());
  760. if (oldHeadId != null && oldHeadLog != null)
  761. assertEquals(oldHeadLog.toString(), db.getReflogReader(
  762. Constants.HEAD).getReverseEntries().toString());
  763. } finally {
  764. lockFile.unlock();
  765. }
  766. }
  767. private void assertExists(boolean positive, String toName) {
  768. assertEquals(toName + (positive ? " " : " does not ") + "exist",
  769. positive, new File(db.getDirectory(), toName).exists());
  770. }
  771. @Test
  772. public void testRenameBranchCannotLockAFileHEADisFromLockHEAD()
  773. throws IOException {
  774. tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name",
  775. "refs/heads/b");
  776. }
  777. @Test
  778. public void testRenameBranchCannotLockAFileHEADisFromLockFrom()
  779. throws IOException {
  780. tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
  781. "refs/heads/new/name", "refs/heads/b");
  782. }
  783. @Test
  784. public void testRenameBranchCannotLockAFileHEADisFromLockTo()
  785. throws IOException {
  786. tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
  787. "refs/heads/new/name", "refs/heads/b");
  788. }
  789. @Test
  790. public void testRenameBranchCannotLockAFileHEADisToLockFrom()
  791. throws IOException {
  792. tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
  793. "refs/heads/new/name", "refs/heads/new/name");
  794. }
  795. @Test
  796. public void testRenameBranchCannotLockAFileHEADisToLockTo()
  797. throws IOException {
  798. tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
  799. "refs/heads/new/name", "refs/heads/new/name");
  800. }
  801. @Test
  802. public void testRenameBranchCannotLockAFileHEADisOtherLockFrom()
  803. throws IOException {
  804. tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
  805. "refs/heads/new/name", "refs/heads/a");
  806. }
  807. @Test
  808. public void testRenameBranchCannotLockAFileHEADisOtherLockTo()
  809. throws IOException {
  810. tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
  811. "refs/heads/new/name", "refs/heads/a");
  812. }
  813. @Test
  814. public void testRenameRefNameColission1avoided() throws IOException {
  815. // setup
  816. ObjectId rb = db.resolve("refs/heads/b");
  817. writeSymref(Constants.HEAD, "refs/heads/a");
  818. RefUpdate updateRef = db.updateRef("refs/heads/a");
  819. updateRef.setNewObjectId(rb);
  820. updateRef.setRefLogMessage("Setup", false);
  821. assertEquals(Result.FAST_FORWARD, updateRef.update());
  822. ObjectId oldHead = db.resolve(Constants.HEAD);
  823. assertEquals(oldHead, rb); // assumption for this test
  824. writeReflog(db, rb, "Just a message", "refs/heads/a");
  825. assertTrue("internal check, we have a log", new File(db.getDirectory(),
  826. "logs/refs/heads/a").exists());
  827. // Now this is our test
  828. RefRename renameRef = db.renameRef("refs/heads/a", "refs/heads/a/b");
  829. Result result = renameRef.rename();
  830. assertEquals(Result.RENAMED, result);
  831. assertNull(db.resolve("refs/heads/a"));
  832. assertEquals(rb, db.resolve("refs/heads/a/b"));
  833. assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size());
  834. assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b")
  835. .getReverseEntries().get(0).getComment());
  836. assertEquals("Just a message", db.getReflogReader("a/b")
  837. .getReverseEntries().get(1).getComment());
  838. assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries()
  839. .get(2).getComment());
  840. // same thing was logged to HEAD
  841. assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD")
  842. .getReverseEntries().get(0).getComment());
  843. }
  844. @Test
  845. public void testRenameRefNameColission2avoided() throws IOException {
  846. // setup
  847. ObjectId rb = db.resolve("refs/heads/b");
  848. writeSymref(Constants.HEAD, "refs/heads/prefix/a");
  849. RefUpdate updateRef = db.updateRef("refs/heads/prefix/a");
  850. updateRef.setNewObjectId(rb);
  851. updateRef.setRefLogMessage("Setup", false);
  852. updateRef.setForceUpdate(true);
  853. assertEquals(Result.FORCED, updateRef.update());
  854. ObjectId oldHead = db.resolve(Constants.HEAD);
  855. assertEquals(oldHead, rb); // assumption for this test
  856. writeReflog(db, rb, "Just a message", "refs/heads/prefix/a");
  857. assertTrue("internal check, we have a log", new File(db.getDirectory(),
  858. "logs/refs/heads/prefix/a").exists());
  859. // Now this is our test
  860. RefRename renameRef = db.renameRef("refs/heads/prefix/a",
  861. "refs/heads/prefix");
  862. Result result = renameRef.rename();
  863. assertEquals(Result.RENAMED, result);
  864. assertNull(db.resolve("refs/heads/prefix/a"));
  865. assertEquals(rb, db.resolve("refs/heads/prefix"));
  866. assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size());
  867. assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
  868. "prefix").getReverseEntries().get(0).getComment());
  869. assertEquals("Just a message", db.getReflogReader("prefix")
  870. .getReverseEntries().get(1).getComment());
  871. assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries()
  872. .get(2).getComment());
  873. assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
  874. "HEAD").getReverseEntries().get(0).getComment());
  875. }
  876. @Test
  877. public void testCreateMissingObject() throws IOException {
  878. String name = "refs/heads/abc";
  879. ObjectId bad =
  880. ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  881. RefUpdate ru = db.updateRef(name);
  882. ru.setNewObjectId(bad);
  883. Result update = ru.update();
  884. assertEquals(Result.REJECTED_MISSING_OBJECT, update);
  885. Ref ref = db.exactRef(name);
  886. assertNull(ref);
  887. }
  888. @Test
  889. public void testUpdateMissingObject() throws IOException {
  890. String name = "refs/heads/abc";
  891. RefUpdate ru = updateRef(name);
  892. Result update = ru.update();
  893. assertEquals(Result.NEW, update);
  894. ObjectId oldId = ru.getNewObjectId();
  895. ObjectId bad =
  896. ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  897. ru = db.updateRef(name);
  898. ru.setNewObjectId(bad);
  899. update = ru.update();
  900. assertEquals(Result.REJECTED_MISSING_OBJECT, update);
  901. Ref ref = db.exactRef(name);
  902. assertNotNull(ref);
  903. assertEquals(oldId, ref.getObjectId());
  904. }
  905. @Test
  906. public void testForceUpdateMissingObject() throws IOException {
  907. String name = "refs/heads/abc";
  908. RefUpdate ru = updateRef(name);
  909. Result update = ru.update();
  910. assertEquals(Result.NEW, update);
  911. ObjectId oldId = ru.getNewObjectId();
  912. ObjectId bad =
  913. ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
  914. ru = db.updateRef(name);
  915. ru.setNewObjectId(bad);
  916. update = ru.forceUpdate();
  917. assertEquals(Result.REJECTED_MISSING_OBJECT, update);
  918. Ref ref = db.exactRef(name);
  919. assertNotNull(ref);
  920. assertEquals(oldId, ref.getObjectId());
  921. }
  922. private static void writeReflog(Repository db, ObjectId newId, String msg,
  923. String refName) throws IOException {
  924. RefDirectory refs = (RefDirectory) db.getRefDatabase();
  925. RefDirectoryUpdate update = refs.newUpdate(refName, true);
  926. update.setNewObjectId(newId);
  927. refs.log(false, update, msg, true);
  928. }
  929. private static class SubclassedId extends ObjectId {
  930. SubclassedId(AnyObjectId src) {
  931. super(src);
  932. }
  933. }
  934. }