Merge branch 'ref-abstract'
* ref-abstract: Optimize RefAdvertiser performance by avoiding sorting branch: Add -m option to rename a branch Replace writeSymref with RefUpdate.link Rewrite reference handling to be abstract and accurate Create new RefList and RefMap utility types Change-Id: If43aacf5aa4013edbd0a6e84d84c4f9e94de5be0
This commit is contained in:
commit
3103abe4e2
|
@ -47,7 +47,6 @@
|
||||||
import static org.eclipse.jgit.http.server.ServletUtils.send;
|
import static org.eclipse.jgit.http.server.ServletUtils.send;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
|
@ -98,9 +97,9 @@ protected void end() {
|
||||||
adv.init(walk, ADVERTISED);
|
adv.init(walk, ADVERTISED);
|
||||||
adv.setDerefTags(true);
|
adv.setDerefTags(true);
|
||||||
|
|
||||||
Map<String, Ref> refs = new HashMap<String, Ref>(db.getAllRefs());
|
Map<String, Ref> refs = db.getAllRefs();
|
||||||
refs.remove(Constants.HEAD);
|
refs.remove(Constants.HEAD);
|
||||||
adv.send(refs.values());
|
adv.send(refs);
|
||||||
return out.toString().getBytes(Constants.CHARACTER_ENCODING);
|
return out.toString().getBytes(Constants.CHARACTER_ENCODING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,18 +50,19 @@
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.kohsuke.args4j.Argument;
|
|
||||||
import org.kohsuke.args4j.ExampleMode;
|
|
||||||
import org.kohsuke.args4j.Option;
|
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.RefComparator;
|
import org.eclipse.jgit.lib.RefComparator;
|
||||||
|
import org.eclipse.jgit.lib.RefRename;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.lib.RefUpdate.Result;
|
import org.eclipse.jgit.lib.RefUpdate.Result;
|
||||||
import org.eclipse.jgit.pgm.opt.CmdLineParser;
|
import org.eclipse.jgit.pgm.opt.CmdLineParser;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.kohsuke.args4j.Argument;
|
||||||
|
import org.kohsuke.args4j.ExampleMode;
|
||||||
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
@Command(common = true, usage = "List, create, or delete branches")
|
@Command(common = true, usage = "List, create, or delete branches")
|
||||||
class Branch extends TextBuiltin {
|
class Branch extends TextBuiltin {
|
||||||
|
@ -81,6 +82,9 @@ class Branch extends TextBuiltin {
|
||||||
@Option(name = "--create-force", aliases = { "-f" }, usage = "force create branch even exists")
|
@Option(name = "--create-force", aliases = { "-f" }, usage = "force create branch even exists")
|
||||||
private boolean createForce = false;
|
private boolean createForce = false;
|
||||||
|
|
||||||
|
@Option(name = "-m", usage = "move/rename a branch")
|
||||||
|
private boolean rename = false;
|
||||||
|
|
||||||
@Option(name = "--verbose", aliases = { "-v" }, usage = "be verbose")
|
@Option(name = "--verbose", aliases = { "-v" }, usage = "be verbose")
|
||||||
private boolean verbose = false;
|
private boolean verbose = false;
|
||||||
|
|
||||||
|
@ -102,7 +106,36 @@ protected void run() throws Exception {
|
||||||
if (branches.size() > 2)
|
if (branches.size() > 2)
|
||||||
throw die("Too many refs given\n" + new CmdLineParser(this).printExample(ExampleMode.ALL));
|
throw die("Too many refs given\n" + new CmdLineParser(this).printExample(ExampleMode.ALL));
|
||||||
|
|
||||||
if (branches.size() > 0) {
|
if (rename) {
|
||||||
|
String src, dst;
|
||||||
|
if (branches.size() == 1) {
|
||||||
|
final Ref head = db.getRef(Constants.HEAD);
|
||||||
|
if (head != null && head.isSymbolic())
|
||||||
|
src = head.getLeaf().getName();
|
||||||
|
else
|
||||||
|
throw die("Cannot rename detached HEAD");
|
||||||
|
dst = branches.get(0);
|
||||||
|
} else {
|
||||||
|
src = branches.get(0);
|
||||||
|
final Ref old = db.getRef(src);
|
||||||
|
if (old == null)
|
||||||
|
throw die(String.format("%s does not exist", src));
|
||||||
|
if (!old.getName().startsWith(Constants.R_HEADS))
|
||||||
|
throw die(String.format("%s is not a branch", src));
|
||||||
|
src = old.getName();
|
||||||
|
dst = branches.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dst.startsWith(Constants.R_HEADS))
|
||||||
|
dst = Constants.R_HEADS + dst;
|
||||||
|
if (!Repository.isValidRefName(dst))
|
||||||
|
throw die(String.format("%s is not a valid ref name", dst));
|
||||||
|
|
||||||
|
RefRename r = db.renameRef(src, dst);
|
||||||
|
if (r.rename() != Result.RENAMED)
|
||||||
|
throw die(String.format("%s cannot be renamed", src));
|
||||||
|
|
||||||
|
} else if (branches.size() > 0) {
|
||||||
String newHead = branches.get(0);
|
String newHead = branches.get(0);
|
||||||
String startBranch;
|
String startBranch;
|
||||||
if (branches.size() == 2)
|
if (branches.size() == 2)
|
||||||
|
@ -143,7 +176,7 @@ private void list() throws Exception {
|
||||||
Ref head = refs.get(Constants.HEAD);
|
Ref head = refs.get(Constants.HEAD);
|
||||||
// This can happen if HEAD is stillborn
|
// This can happen if HEAD is stillborn
|
||||||
if (head != null) {
|
if (head != null) {
|
||||||
String current = head.getName();
|
String current = head.getLeaf().getName();
|
||||||
if (current.equals(Constants.HEAD))
|
if (current.equals(Constants.HEAD))
|
||||||
addRef("(no branch)", head);
|
addRef("(no branch)", head);
|
||||||
addRefs(refs, Constants.R_HEADS, !remote);
|
addRefs(refs, Constants.R_HEADS, !remote);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2009, Google Inc.
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -164,8 +164,11 @@ private Ref guessHEAD(final FetchResult result) {
|
||||||
private void doCheckout(final Ref branch) throws IOException {
|
private void doCheckout(final Ref branch) throws IOException {
|
||||||
if (branch == null)
|
if (branch == null)
|
||||||
throw die("cannot checkout; no HEAD advertised by remote");
|
throw die("cannot checkout; no HEAD advertised by remote");
|
||||||
if (!Constants.HEAD.equals(branch.getName()))
|
if (!Constants.HEAD.equals(branch.getName())) {
|
||||||
db.writeSymref(Constants.HEAD, branch.getName());
|
RefUpdate u = db.updateRef(Constants.HEAD);
|
||||||
|
u.disableRefLog();
|
||||||
|
u.link(branch.getName());
|
||||||
|
}
|
||||||
|
|
||||||
final Commit commit = db.mapCommit(branch.getObjectId());
|
final Commit commit = db.mapCommit(branch.getObjectId());
|
||||||
final RefUpdate u = db.updateRef(Constants.HEAD);
|
final RefUpdate u = db.updateRef(Constants.HEAD);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
* Copyright (C) 2006-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2006-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
@ -92,7 +93,7 @@ protected void show(final RevCommit c) throws Exception {
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
out.print(" (");
|
out.print(" (");
|
||||||
for (Iterator<Ref> i = list.iterator(); i.hasNext(); ) {
|
for (Iterator<Ref> i = list.iterator(); i.hasNext(); ) {
|
||||||
out.print(i.next().getOrigName());
|
out.print(i.next().getName());
|
||||||
if (i.hasNext())
|
if (i.hasNext())
|
||||||
out.print(" ");
|
out.print(" ");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
* Copyright (C) 2008, Jonas Fonseca <fonseca@diku.dk>
|
* Copyright (C) 2008, Jonas Fonseca <fonseca@diku.dk>
|
||||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
@ -44,21 +45,32 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.pgm;
|
package org.eclipse.jgit.pgm;
|
||||||
|
|
||||||
import java.util.TreeMap;
|
import java.util.Map;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.AnyObjectId;
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefComparator;
|
||||||
|
import org.eclipse.jgit.util.RefMap;
|
||||||
|
|
||||||
class ShowRef extends TextBuiltin {
|
class ShowRef extends TextBuiltin {
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Exception {
|
protected void run() throws Exception {
|
||||||
for (final Ref r : new TreeMap<String, Ref>(db.getAllRefs()).values()) {
|
for (final Ref r : getSortedRefs()) {
|
||||||
show(r.getObjectId(), r.getName());
|
show(r.getObjectId(), r.getName());
|
||||||
if (r.getPeeledObjectId() != null)
|
if (r.getPeeledObjectId() != null)
|
||||||
show(r.getPeeledObjectId(), r.getName() + "^{}");
|
show(r.getPeeledObjectId(), r.getName() + "^{}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Iterable<Ref> getSortedRefs() {
|
||||||
|
Map<String, Ref> all = db.getAllRefs();
|
||||||
|
if (all instanceof RefMap
|
||||||
|
|| (all instanceof SortedMap && ((SortedMap) all).comparator() == null))
|
||||||
|
return all.values();
|
||||||
|
return RefComparator.sort(all.values());
|
||||||
|
}
|
||||||
|
|
||||||
private void show(final AnyObjectId id, final String name) {
|
private void show(final AnyObjectId id, final String name) {
|
||||||
out.print(id.name());
|
out.print(id.name());
|
||||||
out.print('\t');
|
out.print('\t');
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009, Google Inc.
|
* Copyright (C) 2009-2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -63,6 +63,7 @@
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.LockFile;
|
import org.eclipse.jgit.lib.LockFile;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ObjectWriter;
|
import org.eclipse.jgit.lib.ObjectWriter;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
|
@ -303,7 +304,8 @@ private Map<String, Ref> computeNewRefs() throws IOException {
|
||||||
}
|
}
|
||||||
throw new MissingObjectException(id, type);
|
throw new MissingObjectException(id, type);
|
||||||
}
|
}
|
||||||
refs.put(name, new Ref(Ref.Storage.PACKED, name, id));
|
refs.put(name, new ObjectIdRef.Unpeeled(Ref.Storage.PACKED,
|
||||||
|
name, id));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
br.close();
|
br.close();
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class ObjectIdRefTest extends TestCase {
|
||||||
|
private static final ObjectId ID_A = ObjectId
|
||||||
|
.fromString("41eb0d88f833b558bddeb269b7ab77399cdf98ed");
|
||||||
|
|
||||||
|
private static final ObjectId ID_B = ObjectId
|
||||||
|
.fromString("698dd0b8d0c299f080559a1cffc7fe029479a408");
|
||||||
|
|
||||||
|
private static final String name = "refs/heads/a.test.ref";
|
||||||
|
|
||||||
|
public void testConstructor_PeeledStatusNotKnown() {
|
||||||
|
ObjectIdRef r;
|
||||||
|
|
||||||
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
|
||||||
|
assertSame(Ref.Storage.LOOSE, r.getStorage());
|
||||||
|
assertSame(name, r.getName());
|
||||||
|
assertSame(ID_A, r.getObjectId());
|
||||||
|
assertFalse("not peeled", r.isPeeled());
|
||||||
|
assertNull("no peel id", r.getPeeledObjectId());
|
||||||
|
assertSame("leaf is this", r, r.getLeaf());
|
||||||
|
assertSame("target is this", r, r.getTarget());
|
||||||
|
assertFalse("not symbolic", r.isSymbolic());
|
||||||
|
|
||||||
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, name, ID_A);
|
||||||
|
assertSame(Ref.Storage.PACKED, r.getStorage());
|
||||||
|
|
||||||
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE_PACKED, name, ID_A);
|
||||||
|
assertSame(Ref.Storage.LOOSE_PACKED, r.getStorage());
|
||||||
|
|
||||||
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, name, null);
|
||||||
|
assertSame(Ref.Storage.NEW, r.getStorage());
|
||||||
|
assertSame(name, r.getName());
|
||||||
|
assertNull("no id on new ref", r.getObjectId());
|
||||||
|
assertFalse("not peeled", r.isPeeled());
|
||||||
|
assertNull("no peel id", r.getPeeledObjectId());
|
||||||
|
assertSame("leaf is this", r, r.getLeaf());
|
||||||
|
assertSame("target is this", r, r.getTarget());
|
||||||
|
assertFalse("not symbolic", r.isSymbolic());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConstructor_Peeled() {
|
||||||
|
ObjectIdRef r;
|
||||||
|
|
||||||
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
|
||||||
|
assertSame(Ref.Storage.LOOSE, r.getStorage());
|
||||||
|
assertSame(name, r.getName());
|
||||||
|
assertSame(ID_A, r.getObjectId());
|
||||||
|
assertFalse("not peeled", r.isPeeled());
|
||||||
|
assertNull("no peel id", r.getPeeledObjectId());
|
||||||
|
assertSame("leaf is this", r, r.getLeaf());
|
||||||
|
assertSame("target is this", r, r.getTarget());
|
||||||
|
assertFalse("not symbolic", r.isSymbolic());
|
||||||
|
|
||||||
|
r = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, name, ID_A);
|
||||||
|
assertTrue("is peeled", r.isPeeled());
|
||||||
|
assertNull("no peel id", r.getPeeledObjectId());
|
||||||
|
|
||||||
|
r = new ObjectIdRef.PeeledTag(Ref.Storage.LOOSE, name, ID_A, ID_B);
|
||||||
|
assertTrue("is peeled", r.isPeeled());
|
||||||
|
assertSame(ID_B, r.getPeeledObjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString() {
|
||||||
|
ObjectIdRef r;
|
||||||
|
|
||||||
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
|
||||||
|
assertEquals("Ref[" + name + "=" + ID_A.name() + "]", r.toString());
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2009-2010, Google Inc.
|
||||||
* Copyright (C) 2009, Robin Rosenberg
|
* Copyright (C) 2009, Robin Rosenberg
|
||||||
* Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
@ -58,15 +59,26 @@
|
||||||
*/
|
*/
|
||||||
public class RefTest extends SampleDataRepositoryTestCase {
|
public class RefTest extends SampleDataRepositoryTestCase {
|
||||||
|
|
||||||
|
private void writeSymref(String src, String dst) throws IOException {
|
||||||
|
RefUpdate u = db.updateRef(src);
|
||||||
|
switch (u.link(dst)) {
|
||||||
|
case NEW:
|
||||||
|
case FORCED:
|
||||||
|
case NO_CHANGE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fail("link " + src + " to " + dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testReadAllIncludingSymrefs() throws Exception {
|
public void testReadAllIncludingSymrefs() throws Exception {
|
||||||
ObjectId masterId = db.resolve("refs/heads/master");
|
ObjectId masterId = db.resolve("refs/heads/master");
|
||||||
RefUpdate updateRef = db.updateRef("refs/remotes/origin/master");
|
RefUpdate updateRef = db.updateRef("refs/remotes/origin/master");
|
||||||
updateRef.setNewObjectId(masterId);
|
updateRef.setNewObjectId(masterId);
|
||||||
updateRef.setForceUpdate(true);
|
updateRef.setForceUpdate(true);
|
||||||
updateRef.update();
|
updateRef.update();
|
||||||
db
|
writeSymref("refs/remotes/origin/HEAD",
|
||||||
.writeSymref("refs/remotes/origin/HEAD",
|
"refs/remotes/origin/master");
|
||||||
"refs/remotes/origin/master");
|
|
||||||
|
|
||||||
ObjectId r = db.resolve("refs/remotes/origin/HEAD");
|
ObjectId r = db.resolve("refs/remotes/origin/HEAD");
|
||||||
assertEquals(masterId, r);
|
assertEquals(masterId, r);
|
||||||
|
@ -75,7 +87,7 @@ public void testReadAllIncludingSymrefs() throws Exception {
|
||||||
Ref refHEAD = allRefs.get("refs/remotes/origin/HEAD");
|
Ref refHEAD = allRefs.get("refs/remotes/origin/HEAD");
|
||||||
assertNotNull(refHEAD);
|
assertNotNull(refHEAD);
|
||||||
assertEquals(masterId, refHEAD.getObjectId());
|
assertEquals(masterId, refHEAD.getObjectId());
|
||||||
assertTrue(refHEAD.isPeeled());
|
assertFalse(refHEAD.isPeeled());
|
||||||
assertNull(refHEAD.getPeeledObjectId());
|
assertNull(refHEAD.getPeeledObjectId());
|
||||||
|
|
||||||
Ref refmaster = allRefs.get("refs/remotes/origin/master");
|
Ref refmaster = allRefs.get("refs/remotes/origin/master");
|
||||||
|
@ -85,9 +97,13 @@ public void testReadAllIncludingSymrefs() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSymRefToPacked() throws IOException {
|
public void testReadSymRefToPacked() throws IOException {
|
||||||
db.writeSymref("HEAD", "refs/heads/b");
|
writeSymref("HEAD", "refs/heads/b");
|
||||||
Ref ref = db.getRef("HEAD");
|
Ref ref = db.getRef("HEAD");
|
||||||
assertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage());
|
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
|
||||||
|
assertTrue("is symref", ref.isSymbolic());
|
||||||
|
ref = ref.getTarget();
|
||||||
|
assertEquals("refs/heads/b", ref.getName());
|
||||||
|
assertEquals(Ref.Storage.PACKED, ref.getStorage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSymRefToLoosePacked() throws IOException {
|
public void testReadSymRefToLoosePacked() throws IOException {
|
||||||
|
@ -98,9 +114,12 @@ public void testReadSymRefToLoosePacked() throws IOException {
|
||||||
Result update = updateRef.update();
|
Result update = updateRef.update();
|
||||||
assertEquals(Result.FORCED, update); // internal
|
assertEquals(Result.FORCED, update); // internal
|
||||||
|
|
||||||
db.writeSymref("HEAD", "refs/heads/master");
|
writeSymref("HEAD", "refs/heads/master");
|
||||||
Ref ref = db.getRef("HEAD");
|
Ref ref = db.getRef("HEAD");
|
||||||
assertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage());
|
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
|
||||||
|
ref = ref.getTarget();
|
||||||
|
assertEquals("refs/heads/master", ref.getName());
|
||||||
|
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadLooseRef() throws IOException {
|
public void testReadLooseRef() throws IOException {
|
||||||
|
@ -129,7 +148,7 @@ public void testReadLoosePackedRef() throws IOException,
|
||||||
os.close();
|
os.close();
|
||||||
|
|
||||||
ref = db.getRef("refs/heads/master");
|
ref = db.getRef("refs/heads/master");
|
||||||
assertEquals(Storage.LOOSE_PACKED, ref.getStorage());
|
assertEquals(Storage.LOOSE, ref.getStorage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,18 +168,26 @@ public void testReadSimplePackedRefSameRepo() throws IOException {
|
||||||
assertEquals(Result.FORCED, update);
|
assertEquals(Result.FORCED, update);
|
||||||
|
|
||||||
ref = db.getRef("refs/heads/master");
|
ref = db.getRef("refs/heads/master");
|
||||||
assertEquals(Storage.LOOSE_PACKED, ref.getStorage());
|
assertEquals(Storage.LOOSE, ref.getStorage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOrigResolvedNamesBranch() throws IOException {
|
public void testResolvedNamesBranch() throws IOException {
|
||||||
Ref ref = db.getRef("a");
|
Ref ref = db.getRef("a");
|
||||||
assertEquals("refs/heads/a", ref.getName());
|
assertEquals("refs/heads/a", ref.getName());
|
||||||
assertEquals("refs/heads/a", ref.getOrigName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOrigResolvedNamesSymRef() throws IOException {
|
public void testResolvedSymRef() throws IOException {
|
||||||
Ref ref = db.getRef("HEAD");
|
Ref ref = db.getRef(Constants.HEAD);
|
||||||
assertEquals("refs/heads/master", ref.getName());
|
assertEquals(Constants.HEAD, ref.getName());
|
||||||
assertEquals("HEAD", ref.getOrigName());
|
assertTrue("is symbolic ref", ref.isSymbolic());
|
||||||
|
assertSame(Ref.Storage.LOOSE, ref.getStorage());
|
||||||
|
|
||||||
|
Ref dst = ref.getTarget();
|
||||||
|
assertNotNull("has target", dst);
|
||||||
|
assertEquals("refs/heads/master", dst.getName());
|
||||||
|
|
||||||
|
assertSame(dst.getObjectId(), ref.getObjectId());
|
||||||
|
assertSame(dst.getPeeledObjectId(), ref.getPeeledObjectId());
|
||||||
|
assertEquals(dst.isPeeled(), ref.isPeeled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
|
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
|
||||||
|
* Copyright (C) 2009-2010, Google Inc.
|
||||||
* Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
|
@ -56,6 +57,18 @@
|
||||||
|
|
||||||
public class RefUpdateTest extends SampleDataRepositoryTestCase {
|
public class RefUpdateTest extends SampleDataRepositoryTestCase {
|
||||||
|
|
||||||
|
private void writeSymref(String src, String dst) throws IOException {
|
||||||
|
RefUpdate u = db.updateRef(src);
|
||||||
|
switch (u.link(dst)) {
|
||||||
|
case NEW:
|
||||||
|
case FORCED:
|
||||||
|
case NO_CHANGE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fail("link " + src + " to " + dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private RefUpdate updateRef(final String name) throws IOException {
|
private RefUpdate updateRef(final String name) throws IOException {
|
||||||
final RefUpdate ref = db.updateRef(name);
|
final RefUpdate ref = db.updateRef(name);
|
||||||
ref.setNewObjectId(db.resolve(Constants.HEAD));
|
ref.setNewObjectId(db.resolve(Constants.HEAD));
|
||||||
|
@ -260,10 +273,10 @@ public void testDeleteForce() throws IOException {
|
||||||
delete(ref, Result.FORCED);
|
delete(ref, Result.FORCED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRefKeySameAsOrigName() {
|
public void testRefKeySameAsName() {
|
||||||
Map<String, Ref> allRefs = db.getAllRefs();
|
Map<String, Ref> allRefs = db.getAllRefs();
|
||||||
for (Entry<String, Ref> e : allRefs.entrySet()) {
|
for (Entry<String, Ref> e : allRefs.entrySet()) {
|
||||||
assertEquals(e.getKey(), e.getValue().getOrigName());
|
assertEquals(e.getKey(), e.getValue().getName());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +321,7 @@ public void testUpdateRefDetached() throws Exception {
|
||||||
assertEquals(ppid, db.resolve("HEAD"));
|
assertEquals(ppid, db.resolve("HEAD"));
|
||||||
Ref ref = db.getRef("HEAD");
|
Ref ref = db.getRef("HEAD");
|
||||||
assertEquals("HEAD", ref.getName());
|
assertEquals("HEAD", ref.getName());
|
||||||
assertEquals("HEAD", ref.getOrigName());
|
assertTrue("is detached", !ref.isSymbolic());
|
||||||
|
|
||||||
// the branch HEAD referred to is left untouched
|
// the branch HEAD referred to is left untouched
|
||||||
assertEquals(pid, db.resolve("refs/heads/master"));
|
assertEquals(pid, db.resolve("refs/heads/master"));
|
||||||
|
@ -328,7 +341,7 @@ public void testUpdateRefDetached() throws Exception {
|
||||||
*/
|
*/
|
||||||
public void testUpdateRefDetachedUnbornHead() throws Exception {
|
public void testUpdateRefDetachedUnbornHead() throws Exception {
|
||||||
ObjectId ppid = db.resolve("refs/heads/master^");
|
ObjectId ppid = db.resolve("refs/heads/master^");
|
||||||
db.writeSymref("HEAD", "refs/heads/unborn");
|
writeSymref("HEAD", "refs/heads/unborn");
|
||||||
RefUpdate updateRef = db.updateRef("HEAD", true);
|
RefUpdate updateRef = db.updateRef("HEAD", true);
|
||||||
updateRef.setForceUpdate(true);
|
updateRef.setForceUpdate(true);
|
||||||
updateRef.setNewObjectId(ppid);
|
updateRef.setNewObjectId(ppid);
|
||||||
|
@ -337,7 +350,7 @@ public void testUpdateRefDetachedUnbornHead() throws Exception {
|
||||||
assertEquals(ppid, db.resolve("HEAD"));
|
assertEquals(ppid, db.resolve("HEAD"));
|
||||||
Ref ref = db.getRef("HEAD");
|
Ref ref = db.getRef("HEAD");
|
||||||
assertEquals("HEAD", ref.getName());
|
assertEquals("HEAD", ref.getName());
|
||||||
assertEquals("HEAD", ref.getOrigName());
|
assertTrue("is detached", !ref.isSymbolic());
|
||||||
|
|
||||||
// the branch HEAD referred to is left untouched
|
// the branch HEAD referred to is left untouched
|
||||||
assertNull(db.resolve("refs/heads/unborn"));
|
assertNull(db.resolve("refs/heads/unborn"));
|
||||||
|
@ -414,11 +427,14 @@ public void testRefsCacheAfterUpdate() throws Exception {
|
||||||
updateRef.setNewObjectId(oldValue);
|
updateRef.setNewObjectId(oldValue);
|
||||||
update = updateRef.update();
|
update = updateRef.update();
|
||||||
assertEquals(Result.FAST_FORWARD, update);
|
assertEquals(Result.FAST_FORWARD, update);
|
||||||
|
|
||||||
allRefs = db.getAllRefs();
|
allRefs = db.getAllRefs();
|
||||||
assertEquals("refs/heads/master", allRefs.get("refs/heads/master").getName());
|
Ref master = allRefs.get("refs/heads/master");
|
||||||
assertEquals("refs/heads/master", allRefs.get("refs/heads/master").getOrigName());
|
Ref head = allRefs.get("HEAD");
|
||||||
assertEquals("refs/heads/master", allRefs.get("HEAD").getName());
|
assertEquals("refs/heads/master", master.getName());
|
||||||
assertEquals("HEAD", allRefs.get("HEAD").getOrigName());
|
assertEquals("HEAD", head.getName());
|
||||||
|
assertTrue("is symbolic reference", head.isSymbolic());
|
||||||
|
assertSame(master, head.getTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -430,21 +446,24 @@ public void testRefsCacheAfterUpdate() throws Exception {
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void testRefsCacheAfterUpdateLoosOnly() throws Exception {
|
public void testRefsCacheAfterUpdateLooseOnly() throws Exception {
|
||||||
// Do not use the defalt repo for this case.
|
// Do not use the defalt repo for this case.
|
||||||
Map<String, Ref> allRefs = db.getAllRefs();
|
Map<String, Ref> allRefs = db.getAllRefs();
|
||||||
ObjectId oldValue = db.resolve("HEAD");
|
ObjectId oldValue = db.resolve("HEAD");
|
||||||
db.writeSymref(Constants.HEAD, "refs/heads/newref");
|
writeSymref(Constants.HEAD, "refs/heads/newref");
|
||||||
RefUpdate updateRef = db.updateRef(Constants.HEAD);
|
RefUpdate updateRef = db.updateRef(Constants.HEAD);
|
||||||
updateRef.setForceUpdate(true);
|
updateRef.setForceUpdate(true);
|
||||||
updateRef.setNewObjectId(oldValue);
|
updateRef.setNewObjectId(oldValue);
|
||||||
Result update = updateRef.update();
|
Result update = updateRef.update();
|
||||||
assertEquals(Result.NEW, update);
|
assertEquals(Result.NEW, update);
|
||||||
|
|
||||||
allRefs = db.getAllRefs();
|
allRefs = db.getAllRefs();
|
||||||
assertEquals("refs/heads/newref", allRefs.get("HEAD").getName());
|
Ref head = allRefs.get("HEAD");
|
||||||
assertEquals("HEAD", allRefs.get("HEAD").getOrigName());
|
Ref newref = allRefs.get("refs/heads/newref");
|
||||||
assertEquals("refs/heads/newref", allRefs.get("refs/heads/newref").getName());
|
assertEquals("refs/heads/newref", newref.getName());
|
||||||
assertEquals("refs/heads/newref", allRefs.get("refs/heads/newref").getOrigName());
|
assertEquals("HEAD", head.getName());
|
||||||
|
assertTrue("is symbolic reference", head.isSymbolic());
|
||||||
|
assertSame(newref, head.getTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -575,8 +594,8 @@ public void testRenameBranchHasPreviousLog() throws IOException {
|
||||||
ObjectId oldHead = db.resolve(Constants.HEAD);
|
ObjectId oldHead = db.resolve(Constants.HEAD);
|
||||||
assertFalse("precondition for this test, branch b != HEAD", rb
|
assertFalse("precondition for this test, branch b != HEAD", rb
|
||||||
.equals(oldHead));
|
.equals(oldHead));
|
||||||
RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
|
writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
|
||||||
assertTrue("no log on old branch", new File(db.getDirectory(),
|
assertTrue("log on old branch", new File(db.getDirectory(),
|
||||||
"logs/refs/heads/b").exists());
|
"logs/refs/heads/b").exists());
|
||||||
RefRename renameRef = db.renameRef("refs/heads/b",
|
RefRename renameRef = db.renameRef("refs/heads/b",
|
||||||
"refs/heads/new/name");
|
"refs/heads/new/name");
|
||||||
|
@ -595,11 +614,11 @@ public void testRenameBranchHasPreviousLog() throws IOException {
|
||||||
|
|
||||||
public void testRenameCurrentBranch() throws IOException {
|
public void testRenameCurrentBranch() throws IOException {
|
||||||
ObjectId rb = db.resolve("refs/heads/b");
|
ObjectId rb = db.resolve("refs/heads/b");
|
||||||
db.writeSymref(Constants.HEAD, "refs/heads/b");
|
writeSymref(Constants.HEAD, "refs/heads/b");
|
||||||
ObjectId oldHead = db.resolve(Constants.HEAD);
|
ObjectId oldHead = db.resolve(Constants.HEAD);
|
||||||
assertTrue("internal test condition, b == HEAD", rb.equals(oldHead));
|
assertTrue("internal test condition, b == HEAD", rb.equals(oldHead));
|
||||||
RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
|
writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
|
||||||
assertTrue("no log on old branch", new File(db.getDirectory(),
|
assertTrue("log on old branch", new File(db.getDirectory(),
|
||||||
"logs/refs/heads/b").exists());
|
"logs/refs/heads/b").exists());
|
||||||
RefRename renameRef = db.renameRef("refs/heads/b",
|
RefRename renameRef = db.renameRef("refs/heads/b",
|
||||||
"refs/heads/new/name");
|
"refs/heads/new/name");
|
||||||
|
@ -625,10 +644,9 @@ public void testRenameBranchAlsoInPack() throws IOException {
|
||||||
updateRef.setForceUpdate(true);
|
updateRef.setForceUpdate(true);
|
||||||
Result update = updateRef.update();
|
Result update = updateRef.update();
|
||||||
assertEquals("internal check new ref is loose", Result.FORCED, update);
|
assertEquals("internal check new ref is loose", Result.FORCED, update);
|
||||||
assertEquals(Ref.Storage.LOOSE_PACKED, db.getRef("refs/heads/b")
|
assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage());
|
||||||
.getStorage());
|
writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
|
||||||
RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
|
assertTrue("log on old branch", new File(db.getDirectory(),
|
||||||
assertTrue("no log on old branch", new File(db.getDirectory(),
|
|
||||||
"logs/refs/heads/b").exists());
|
"logs/refs/heads/b").exists());
|
||||||
RefRename renameRef = db.renameRef("refs/heads/b",
|
RefRename renameRef = db.renameRef("refs/heads/b",
|
||||||
"refs/heads/new/name");
|
"refs/heads/new/name");
|
||||||
|
@ -654,10 +672,10 @@ public void testRenameBranchAlsoInPack() throws IOException {
|
||||||
public void tryRenameWhenLocked(String toLock, String fromName,
|
public void tryRenameWhenLocked(String toLock, String fromName,
|
||||||
String toName, String headPointsTo) throws IOException {
|
String toName, String headPointsTo) throws IOException {
|
||||||
// setup
|
// setup
|
||||||
db.writeSymref(Constants.HEAD, headPointsTo);
|
writeSymref(Constants.HEAD, headPointsTo);
|
||||||
ObjectId oldfromId = db.resolve(fromName);
|
ObjectId oldfromId = db.resolve(fromName);
|
||||||
ObjectId oldHeadId = db.resolve(Constants.HEAD);
|
ObjectId oldHeadId = db.resolve(Constants.HEAD);
|
||||||
RefLogWriter.writeReflog(db, oldfromId, oldfromId, "Just a message",
|
writeReflog(db, oldfromId, oldfromId, "Just a message",
|
||||||
fromName);
|
fromName);
|
||||||
List<org.eclipse.jgit.lib.ReflogReader.Entry> oldFromLog = db
|
List<org.eclipse.jgit.lib.ReflogReader.Entry> oldFromLog = db
|
||||||
.getReflogReader(fromName).getReverseEntries();
|
.getReflogReader(fromName).getReverseEntries();
|
||||||
|
@ -691,8 +709,8 @@ public void tryRenameWhenLocked(String toLock, String fromName,
|
||||||
assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
|
assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
|
||||||
.getReverseEntries().toString());
|
.getReverseEntries().toString());
|
||||||
if (oldHeadId != null)
|
if (oldHeadId != null)
|
||||||
assertEquals(oldHeadLog, db.getReflogReader(Constants.HEAD)
|
assertEquals(oldHeadLog.toString(), db.getReflogReader(
|
||||||
.getReverseEntries());
|
Constants.HEAD).getReverseEntries().toString());
|
||||||
} finally {
|
} finally {
|
||||||
lockFile.unlock();
|
lockFile.unlock();
|
||||||
}
|
}
|
||||||
|
@ -733,12 +751,6 @@ public void testRenameBranchCannotLockAFileHEADisToLockTo()
|
||||||
"refs/heads/new/name", "refs/heads/new/name");
|
"refs/heads/new/name", "refs/heads/new/name");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRenameBranchCannotLockAFileHEADisToLockTmp()
|
|
||||||
throws IOException {
|
|
||||||
tryRenameWhenLocked("RENAMED-REF.." + Thread.currentThread().getId(),
|
|
||||||
"refs/heads/b", "refs/heads/new/name", "refs/heads/new/name");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRenameBranchCannotLockAFileHEADisOtherLockFrom()
|
public void testRenameBranchCannotLockAFileHEADisOtherLockFrom()
|
||||||
throws IOException {
|
throws IOException {
|
||||||
tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
|
tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
|
||||||
|
@ -751,23 +763,17 @@ public void testRenameBranchCannotLockAFileHEADisOtherLockTo()
|
||||||
"refs/heads/new/name", "refs/heads/a");
|
"refs/heads/new/name", "refs/heads/a");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRenameBranchCannotLockAFileHEADisOtherLockTmp()
|
|
||||||
throws IOException {
|
|
||||||
tryRenameWhenLocked("RENAMED-REF.." + Thread.currentThread().getId(),
|
|
||||||
"refs/heads/b", "refs/heads/new/name", "refs/heads/a");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRenameRefNameColission1avoided() throws IOException {
|
public void testRenameRefNameColission1avoided() throws IOException {
|
||||||
// setup
|
// setup
|
||||||
ObjectId rb = db.resolve("refs/heads/b");
|
ObjectId rb = db.resolve("refs/heads/b");
|
||||||
db.writeSymref(Constants.HEAD, "refs/heads/a");
|
writeSymref(Constants.HEAD, "refs/heads/a");
|
||||||
RefUpdate updateRef = db.updateRef("refs/heads/a");
|
RefUpdate updateRef = db.updateRef("refs/heads/a");
|
||||||
updateRef.setNewObjectId(rb);
|
updateRef.setNewObjectId(rb);
|
||||||
updateRef.setRefLogMessage("Setup", false);
|
updateRef.setRefLogMessage("Setup", false);
|
||||||
assertEquals(Result.FAST_FORWARD, updateRef.update());
|
assertEquals(Result.FAST_FORWARD, updateRef.update());
|
||||||
ObjectId oldHead = db.resolve(Constants.HEAD);
|
ObjectId oldHead = db.resolve(Constants.HEAD);
|
||||||
assertTrue(rb.equals(oldHead)); // assumption for this test
|
assertTrue(rb.equals(oldHead)); // assumption for this test
|
||||||
RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/a");
|
writeReflog(db, rb, rb, "Just a message", "refs/heads/a");
|
||||||
assertTrue("internal check, we have a log", new File(db.getDirectory(),
|
assertTrue("internal check, we have a log", new File(db.getDirectory(),
|
||||||
"logs/refs/heads/a").exists());
|
"logs/refs/heads/a").exists());
|
||||||
|
|
||||||
|
@ -792,7 +798,7 @@ public void testRenameRefNameColission1avoided() throws IOException {
|
||||||
public void testRenameRefNameColission2avoided() throws IOException {
|
public void testRenameRefNameColission2avoided() throws IOException {
|
||||||
// setup
|
// setup
|
||||||
ObjectId rb = db.resolve("refs/heads/b");
|
ObjectId rb = db.resolve("refs/heads/b");
|
||||||
db.writeSymref(Constants.HEAD, "refs/heads/prefix/a");
|
writeSymref(Constants.HEAD, "refs/heads/prefix/a");
|
||||||
RefUpdate updateRef = db.updateRef("refs/heads/prefix/a");
|
RefUpdate updateRef = db.updateRef("refs/heads/prefix/a");
|
||||||
updateRef.setNewObjectId(rb);
|
updateRef.setNewObjectId(rb);
|
||||||
updateRef.setRefLogMessage("Setup", false);
|
updateRef.setRefLogMessage("Setup", false);
|
||||||
|
@ -800,7 +806,7 @@ public void testRenameRefNameColission2avoided() throws IOException {
|
||||||
assertEquals(Result.FORCED, updateRef.update());
|
assertEquals(Result.FORCED, updateRef.update());
|
||||||
ObjectId oldHead = db.resolve(Constants.HEAD);
|
ObjectId oldHead = db.resolve(Constants.HEAD);
|
||||||
assertTrue(rb.equals(oldHead)); // assumption for this test
|
assertTrue(rb.equals(oldHead)); // assumption for this test
|
||||||
RefLogWriter.writeReflog(db, rb, rb, "Just a message",
|
writeReflog(db, rb, rb, "Just a message",
|
||||||
"refs/heads/prefix/a");
|
"refs/heads/prefix/a");
|
||||||
assertTrue("internal check, we have a log", new File(db.getDirectory(),
|
assertTrue("internal check, we have a log", new File(db.getDirectory(),
|
||||||
"logs/refs/heads/prefix/a").exists());
|
"logs/refs/heads/prefix/a").exists());
|
||||||
|
@ -823,4 +829,13 @@ public void testRenameRefNameColission2avoided() throws IOException {
|
||||||
assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
|
assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
|
||||||
"HEAD").getReverseEntries().get(0).getComment());
|
"HEAD").getReverseEntries().get(0).getComment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeReflog(Repository db, ObjectId oldId, ObjectId newId,
|
||||||
|
String msg, String refName) throws IOException {
|
||||||
|
RefDirectory refs = (RefDirectory) db.getRefDatabase();
|
||||||
|
RefDirectoryUpdate update = refs.newUpdate(refName, true);
|
||||||
|
update.setOldObjectId(oldId);
|
||||||
|
update.setNewObjectId(newId);
|
||||||
|
refs.log(update, msg, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
|
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
|
||||||
* Copyright (C) 2009, Christian Halstrick, Matthias Sohn, SAP AG
|
* Copyright (C) 2009, Christian Halstrick, Matthias Sohn, SAP AG
|
||||||
* Copyright (C) 2009, Google Inc.
|
* Copyright (C) 2009-2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -54,7 +54,7 @@ public void testlogAllRefUpdates() throws Exception {
|
||||||
|
|
||||||
// check that there are no entries in the reflog and turn off writing
|
// check that there are no entries in the reflog and turn off writing
|
||||||
// reflogs
|
// reflogs
|
||||||
assertNull(db.getReflogReader(Constants.HEAD));
|
assertEquals(0, db.getReflogReader(Constants.HEAD).getReverseEntries().size());
|
||||||
db.getConfig().setBoolean("core", null, "logallrefupdates", false);
|
db.getConfig().setBoolean("core", null, "logallrefupdates", false);
|
||||||
|
|
||||||
// do one commit and check that reflog size is 0: no reflogs should be
|
// do one commit and check that reflog size is 0: no reflogs should be
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class SymbolicRefTest extends TestCase {
|
||||||
|
private static final ObjectId ID_A = ObjectId
|
||||||
|
.fromString("41eb0d88f833b558bddeb269b7ab77399cdf98ed");
|
||||||
|
|
||||||
|
private static final ObjectId ID_B = ObjectId
|
||||||
|
.fromString("698dd0b8d0c299f080559a1cffc7fe029479a408");
|
||||||
|
|
||||||
|
private static final String targetName = "refs/heads/a.test.ref";
|
||||||
|
|
||||||
|
private static final String name = "refs/remotes/origin/HEAD";
|
||||||
|
|
||||||
|
public void testConstructor() {
|
||||||
|
Ref t;
|
||||||
|
SymbolicRef r;
|
||||||
|
|
||||||
|
t = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, targetName, null);
|
||||||
|
r = new SymbolicRef(name, t);
|
||||||
|
assertSame(Ref.Storage.LOOSE, r.getStorage());
|
||||||
|
assertSame(name, r.getName());
|
||||||
|
assertNull("no id on new ref", r.getObjectId());
|
||||||
|
assertFalse("not peeled", r.isPeeled());
|
||||||
|
assertNull("no peel id", r.getPeeledObjectId());
|
||||||
|
assertSame("leaf is t", t, r.getLeaf());
|
||||||
|
assertSame("target is t", t, r.getTarget());
|
||||||
|
assertTrue("is symbolic", r.isSymbolic());
|
||||||
|
|
||||||
|
t = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, targetName, ID_A);
|
||||||
|
r = new SymbolicRef(name, t);
|
||||||
|
assertSame(Ref.Storage.LOOSE, r.getStorage());
|
||||||
|
assertSame(name, r.getName());
|
||||||
|
assertSame(ID_A, r.getObjectId());
|
||||||
|
assertFalse("not peeled", r.isPeeled());
|
||||||
|
assertNull("no peel id", r.getPeeledObjectId());
|
||||||
|
assertSame("leaf is t", t, r.getLeaf());
|
||||||
|
assertSame("target is t", t, r.getTarget());
|
||||||
|
assertTrue("is symbolic", r.isSymbolic());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLeaf() {
|
||||||
|
Ref a;
|
||||||
|
SymbolicRef b, c, d;
|
||||||
|
|
||||||
|
a = new ObjectIdRef.PeeledTag(Ref.Storage.PACKED, targetName, ID_A, ID_B);
|
||||||
|
b = new SymbolicRef("B", a);
|
||||||
|
c = new SymbolicRef("C", b);
|
||||||
|
d = new SymbolicRef("D", c);
|
||||||
|
|
||||||
|
assertSame(c, d.getTarget());
|
||||||
|
assertSame(b, c.getTarget());
|
||||||
|
assertSame(a, b.getTarget());
|
||||||
|
|
||||||
|
assertSame(a, d.getLeaf());
|
||||||
|
assertSame(a, c.getLeaf());
|
||||||
|
assertSame(a, b.getLeaf());
|
||||||
|
assertSame(a, a.getLeaf());
|
||||||
|
|
||||||
|
assertSame(ID_A, d.getObjectId());
|
||||||
|
assertSame(ID_A, c.getObjectId());
|
||||||
|
assertSame(ID_A, b.getObjectId());
|
||||||
|
|
||||||
|
assertTrue(d.isPeeled());
|
||||||
|
assertTrue(c.isPeeled());
|
||||||
|
assertTrue(b.isPeeled());
|
||||||
|
|
||||||
|
assertSame(ID_B, d.getPeeledObjectId());
|
||||||
|
assertSame(ID_B, c.getPeeledObjectId());
|
||||||
|
assertSame(ID_B, b.getPeeledObjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString() {
|
||||||
|
Ref a;
|
||||||
|
SymbolicRef b, c, d;
|
||||||
|
|
||||||
|
a = new ObjectIdRef.PeeledTag(Ref.Storage.PACKED, targetName, ID_A, ID_B);
|
||||||
|
b = new SymbolicRef("B", a);
|
||||||
|
c = new SymbolicRef("C", b);
|
||||||
|
d = new SymbolicRef("D", c);
|
||||||
|
|
||||||
|
assertEquals("SymbolicRef[D -> C -> B -> " + targetName + "="
|
||||||
|
+ ID_A.name() + "]", d.toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -543,6 +543,7 @@ public void test025_packedRefs() throws IOException {
|
||||||
w.println("0ce2ebdb36076ef0b38adbe077a07d43b43e3807 refs/tags/test022");
|
w.println("0ce2ebdb36076ef0b38adbe077a07d43b43e3807 refs/tags/test022");
|
||||||
w.println("^b5d3b45a96b340441f5abb9080411705c51cc86c");
|
w.println("^b5d3b45a96b340441f5abb9080411705c51cc86c");
|
||||||
w.close();
|
w.close();
|
||||||
|
((RefDirectory)db.getRefDatabase()).rescan();
|
||||||
|
|
||||||
Tag mapTag20 = db.mapTag("test020");
|
Tag mapTag20 = db.mapTag("test020");
|
||||||
assertNotNull("have tag test020", mapTag20);
|
assertNotNull("have tag test020", mapTag20);
|
||||||
|
@ -673,6 +674,8 @@ public void test027_UnpackedRefHigherPriorityThanPacked() throws IOException {
|
||||||
public void test028_LockPackedRef() throws IOException {
|
public void test028_LockPackedRef() throws IOException {
|
||||||
writeTrashFile(".git/packed-refs", "7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar");
|
writeTrashFile(".git/packed-refs", "7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar");
|
||||||
writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n");
|
writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n");
|
||||||
|
BUG_WorkAroundRacyGitIssues("packed-refs");
|
||||||
|
BUG_WorkAroundRacyGitIssues("HEAD");
|
||||||
|
|
||||||
ObjectId resolve = db.resolve("HEAD");
|
ObjectId resolve = db.resolve("HEAD");
|
||||||
assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name());
|
assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name());
|
||||||
|
@ -727,4 +730,23 @@ public void test30_stripWorkDir() {
|
||||||
assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkDir(), file));
|
assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkDir(), file));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kick the timestamp of a local file.
|
||||||
|
* <p>
|
||||||
|
* We shouldn't have to make these method calls. The cache is using file
|
||||||
|
* system timestamps, and on many systems unit tests run faster than the
|
||||||
|
* modification clock. Dumping the cache after we make an edit behind
|
||||||
|
* RefDirectory's back allows the tests to pass.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the file in the repository to force a time change on.
|
||||||
|
*/
|
||||||
|
private void BUG_WorkAroundRacyGitIssues(String name) {
|
||||||
|
File path = new File(db.getDirectory(), name);
|
||||||
|
long old = path.lastModified();
|
||||||
|
long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
|
||||||
|
path.setLastModified(set);
|
||||||
|
assertTrue("time changed", old != path.lastModified());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
import org.eclipse.jgit.errors.NotSupportedException;
|
import org.eclipse.jgit.errors.NotSupportedException;
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
@ -88,7 +89,7 @@ public void testUpdateFastForward() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
testOneUpdateStatus(rru, ref, Status.OK, true);
|
testOneUpdateStatus(rru, ref, Status.OK, true);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +104,7 @@ public void testUpdateNonFastForwardUnknownObject() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("0000000000000000000000000000000000000001"));
|
ObjectId.fromString("0000000000000000000000000000000000000001"));
|
||||||
testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
|
testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +119,7 @@ public void testUpdateNonFastForward() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
|
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
||||||
testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
|
testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null);
|
||||||
}
|
}
|
||||||
|
@ -132,7 +133,7 @@ public void testUpdateNonFastForwardForced() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
|
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
|
||||||
"refs/heads/master", true, null, null);
|
"refs/heads/master", true, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
||||||
testOneUpdateStatus(rru, ref, Status.OK, false);
|
testOneUpdateStatus(rru, ref, Status.OK, false);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +158,7 @@ public void testUpdateCreateRef() throws IOException {
|
||||||
public void testUpdateDelete() throws IOException {
|
public void testUpdateDelete() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db, null,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db, null,
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
||||||
testOneUpdateStatus(rru, ref, Status.OK, true);
|
testOneUpdateStatus(rru, ref, Status.OK, true);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +184,7 @@ public void testUpdateUpToDate() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
||||||
testOneUpdateStatus(rru, ref, Status.UP_TO_DATE, null);
|
testOneUpdateStatus(rru, ref, Status.UP_TO_DATE, null);
|
||||||
}
|
}
|
||||||
|
@ -198,7 +199,7 @@ public void testUpdateExpectedRemote() throws IOException {
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, ObjectId
|
"refs/heads/master", false, null, ObjectId
|
||||||
.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
testOneUpdateStatus(rru, ref, Status.OK, true);
|
testOneUpdateStatus(rru, ref, Status.OK, true);
|
||||||
}
|
}
|
||||||
|
@ -214,7 +215,7 @@ public void testUpdateUnexpectedRemote() throws IOException {
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, ObjectId
|
"refs/heads/master", false, null, ObjectId
|
||||||
.fromString("0000000000000000000000000000000000000001"));
|
.fromString("0000000000000000000000000000000000000001"));
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
|
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +232,7 @@ public void testUpdateUnexpectedRemoteVsForce() throws IOException {
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", true, null, ObjectId
|
"refs/heads/master", true, null, ObjectId
|
||||||
.fromString("0000000000000000000000000000000000000001"));
|
.fromString("0000000000000000000000000000000000000001"));
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
|
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
|
||||||
}
|
}
|
||||||
|
@ -246,7 +247,7 @@ public void testUpdateRejectedByConnection() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
testOneUpdateStatus(rru, ref, Status.REJECTED_OTHER_REASON, null);
|
testOneUpdateStatus(rru, ref, Status.REJECTED_OTHER_REASON, null);
|
||||||
}
|
}
|
||||||
|
@ -260,7 +261,7 @@ public void testUpdateRejectedByConnection() throws IOException {
|
||||||
public void testUpdateMixedCases() throws IOException {
|
public void testUpdateMixedCases() throws IOException {
|
||||||
final RemoteRefUpdate rruOk = new RemoteRefUpdate(db, null,
|
final RemoteRefUpdate rruOk = new RemoteRefUpdate(db, null,
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref refToChange = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref refToChange = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
||||||
final RemoteRefUpdate rruReject = new RemoteRefUpdate(db, null,
|
final RemoteRefUpdate rruReject = new RemoteRefUpdate(db, null,
|
||||||
"refs/heads/nonexisting", false, null, null);
|
"refs/heads/nonexisting", false, null, null);
|
||||||
|
@ -282,7 +283,7 @@ public void testTrackingRefUpdateEnabled() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, "refs/remotes/test/master", null);
|
"refs/heads/master", false, "refs/remotes/test/master", null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
refUpdates.add(rru);
|
refUpdates.add(rru);
|
||||||
advertisedRefs.add(ref);
|
advertisedRefs.add(ref);
|
||||||
|
@ -303,7 +304,7 @@ public void testTrackingRefUpdateDisabled() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
refUpdates.add(rru);
|
refUpdates.add(rru);
|
||||||
advertisedRefs.add(ref);
|
advertisedRefs.add(ref);
|
||||||
|
@ -320,7 +321,7 @@ public void testTrackingRefUpdateOnReject() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
|
"ac7e7e44c1885efb472ad54a78327d66bfc4ecef",
|
||||||
"refs/heads/master", false, null, null);
|
"refs/heads/master", false, null, null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9"));
|
||||||
final PushResult result = testOneUpdateStatus(rru, ref,
|
final PushResult result = testOneUpdateStatus(rru, ref,
|
||||||
Status.REJECTED_NONFASTFORWARD, null);
|
Status.REJECTED_NONFASTFORWARD, null);
|
||||||
|
@ -336,7 +337,7 @@ public void testPushResult() throws IOException {
|
||||||
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
final RemoteRefUpdate rru = new RemoteRefUpdate(db,
|
||||||
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
"2c349335b7f797072cf729c4f3bb0914ecb6dec9",
|
||||||
"refs/heads/master", false, "refs/remotes/test/master", null);
|
"refs/heads/master", false, "refs/remotes/test/master", null);
|
||||||
final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
refUpdates.add(rru);
|
refUpdates.add(rru);
|
||||||
advertisedRefs.add(ref);
|
advertisedRefs.add(ref);
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
|
||||||
public class RefSpecTest extends TestCase {
|
public class RefSpecTest extends TestCase {
|
||||||
|
@ -59,12 +60,12 @@ public void testMasterMaster() {
|
||||||
assertEquals(sn + ":" + sn, rs.toString());
|
assertEquals(sn + ":" + sn, rs.toString());
|
||||||
assertEquals(rs, new RefSpec(rs.toString()));
|
assertEquals(rs, new RefSpec(rs.toString()));
|
||||||
|
|
||||||
Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
|
Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
|
||||||
assertTrue(rs.matchSource(r));
|
assertTrue(rs.matchSource(r));
|
||||||
assertTrue(rs.matchDestination(r));
|
assertTrue(rs.matchDestination(r));
|
||||||
assertSame(rs, rs.expandFromSource(r));
|
assertSame(rs, rs.expandFromSource(r));
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
}
|
}
|
||||||
|
@ -91,12 +92,12 @@ public void testForceMasterMaster() {
|
||||||
assertEquals("+" + sn + ":" + sn, rs.toString());
|
assertEquals("+" + sn + ":" + sn, rs.toString());
|
||||||
assertEquals(rs, new RefSpec(rs.toString()));
|
assertEquals(rs, new RefSpec(rs.toString()));
|
||||||
|
|
||||||
Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
|
Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
|
||||||
assertTrue(rs.matchSource(r));
|
assertTrue(rs.matchSource(r));
|
||||||
assertTrue(rs.matchDestination(r));
|
assertTrue(rs.matchDestination(r));
|
||||||
assertSame(rs, rs.expandFromSource(r));
|
assertSame(rs, rs.expandFromSource(r));
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
}
|
}
|
||||||
|
@ -111,12 +112,12 @@ public void testMaster() {
|
||||||
assertEquals(sn, rs.toString());
|
assertEquals(sn, rs.toString());
|
||||||
assertEquals(rs, new RefSpec(rs.toString()));
|
assertEquals(rs, new RefSpec(rs.toString()));
|
||||||
|
|
||||||
Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
|
Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
|
||||||
assertTrue(rs.matchSource(r));
|
assertTrue(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
assertSame(rs, rs.expandFromSource(r));
|
assertSame(rs, rs.expandFromSource(r));
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
}
|
}
|
||||||
|
@ -131,12 +132,12 @@ public void testForceMaster() {
|
||||||
assertEquals("+" + sn, rs.toString());
|
assertEquals("+" + sn, rs.toString());
|
||||||
assertEquals(rs, new RefSpec(rs.toString()));
|
assertEquals(rs, new RefSpec(rs.toString()));
|
||||||
|
|
||||||
Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
|
Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
|
||||||
assertTrue(rs.matchSource(r));
|
assertTrue(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
assertSame(rs, rs.expandFromSource(r));
|
assertSame(rs, rs.expandFromSource(r));
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
}
|
}
|
||||||
|
@ -151,12 +152,12 @@ public void testDeleteMaster() {
|
||||||
assertEquals(":" + sn, rs.toString());
|
assertEquals(":" + sn, rs.toString());
|
||||||
assertEquals(rs, new RefSpec(rs.toString()));
|
assertEquals(rs, new RefSpec(rs.toString()));
|
||||||
|
|
||||||
Ref r = new Ref(Ref.Storage.LOOSE, sn, null);
|
Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn, null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertTrue(rs.matchDestination(r));
|
assertTrue(rs.matchDestination(r));
|
||||||
assertSame(rs, rs.expandFromSource(r));
|
assertSame(rs, rs.expandFromSource(r));
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, sn + "-and-more", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
}
|
}
|
||||||
|
@ -175,7 +176,7 @@ public void testForceRemotesOrigin() {
|
||||||
Ref r;
|
Ref r;
|
||||||
RefSpec expanded;
|
RefSpec expanded;
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, "refs/heads/master", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master", null);
|
||||||
assertTrue(rs.matchSource(r));
|
assertTrue(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
expanded = rs.expandFromSource(r);
|
expanded = rs.expandFromSource(r);
|
||||||
|
@ -185,11 +186,11 @@ public void testForceRemotesOrigin() {
|
||||||
assertEquals(r.getName(), expanded.getSource());
|
assertEquals(r.getName(), expanded.getSource());
|
||||||
assertEquals("refs/remotes/origin/master", expanded.getDestination());
|
assertEquals("refs/remotes/origin/master", expanded.getDestination());
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, "refs/remotes/origin/next", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/remotes/origin/next", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertTrue(rs.matchDestination(r));
|
assertTrue(rs.matchDestination(r));
|
||||||
|
|
||||||
r = new Ref(Ref.Storage.LOOSE, "refs/tags/v1.0", null);
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/tags/v1.0", null);
|
||||||
assertFalse(rs.matchSource(r));
|
assertFalse(rs.matchSource(r));
|
||||||
assertFalse(rs.matchDestination(r));
|
assertFalse(rs.matchDestination(r));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,432 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.util;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
|
||||||
|
public class RefListTest extends TestCase {
|
||||||
|
private static final ObjectId ID = ObjectId
|
||||||
|
.fromString("41eb0d88f833b558bddeb269b7ab77399cdf98ed");
|
||||||
|
|
||||||
|
private static final Ref REF_A = newRef("A");
|
||||||
|
|
||||||
|
private static final Ref REF_B = newRef("B");
|
||||||
|
|
||||||
|
private static final Ref REF_c = newRef("c");
|
||||||
|
|
||||||
|
public void testEmpty() {
|
||||||
|
RefList<Ref> list = RefList.emptyList();
|
||||||
|
assertEquals(0, list.size());
|
||||||
|
assertTrue(list.isEmpty());
|
||||||
|
assertFalse(list.iterator().hasNext());
|
||||||
|
assertEquals(-1, list.find("a"));
|
||||||
|
assertEquals(-1, list.find("z"));
|
||||||
|
assertFalse(list.contains("a"));
|
||||||
|
assertNull(list.get("a"));
|
||||||
|
try {
|
||||||
|
list.get(0);
|
||||||
|
fail("RefList.emptyList should have 0 element array");
|
||||||
|
} catch (ArrayIndexOutOfBoundsException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmptyBuilder() {
|
||||||
|
RefList<Ref> list = new RefList.Builder<Ref>().toRefList();
|
||||||
|
assertEquals(0, list.size());
|
||||||
|
assertFalse(list.iterator().hasNext());
|
||||||
|
assertEquals(-1, list.find("a"));
|
||||||
|
assertEquals(-1, list.find("z"));
|
||||||
|
assertFalse(list.contains("a"));
|
||||||
|
assertNull(list.get("a"));
|
||||||
|
assertTrue(list.asList().isEmpty());
|
||||||
|
assertEquals("[]", list.toString());
|
||||||
|
|
||||||
|
// default array capacity should be 16, with no bounds checking.
|
||||||
|
assertNull(list.get(16 - 1));
|
||||||
|
try {
|
||||||
|
list.get(16);
|
||||||
|
fail("default RefList should have 16 element array");
|
||||||
|
} catch (ArrayIndexOutOfBoundsException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBuilder_AddThenSort() {
|
||||||
|
RefList.Builder<Ref> builder = new RefList.Builder<Ref>(1);
|
||||||
|
builder.add(REF_B);
|
||||||
|
builder.add(REF_A);
|
||||||
|
|
||||||
|
RefList<Ref> list = builder.toRefList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertSame(REF_B, list.get(0));
|
||||||
|
assertSame(REF_A, list.get(1));
|
||||||
|
|
||||||
|
builder.sort();
|
||||||
|
list = builder.toRefList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertSame(REF_A, list.get(0));
|
||||||
|
assertSame(REF_B, list.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBuilder_AddAll() {
|
||||||
|
RefList.Builder<Ref> builder = new RefList.Builder<Ref>(1);
|
||||||
|
Ref[] src = { REF_A, REF_B, REF_c, REF_A };
|
||||||
|
builder.addAll(src, 1, 2);
|
||||||
|
|
||||||
|
RefList<Ref> list = builder.toRefList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertSame(REF_B, list.get(0));
|
||||||
|
assertSame(REF_c, list.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBuilder_Set() {
|
||||||
|
RefList.Builder<Ref> builder = new RefList.Builder<Ref>();
|
||||||
|
builder.add(REF_A);
|
||||||
|
builder.add(REF_A);
|
||||||
|
|
||||||
|
assertEquals(2, builder.size());
|
||||||
|
assertSame(REF_A, builder.get(0));
|
||||||
|
assertSame(REF_A, builder.get(1));
|
||||||
|
|
||||||
|
RefList<Ref> list = builder.toRefList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertSame(REF_A, list.get(0));
|
||||||
|
assertSame(REF_A, list.get(1));
|
||||||
|
builder.set(1, REF_B);
|
||||||
|
|
||||||
|
list = builder.toRefList();
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
assertSame(REF_A, list.get(0));
|
||||||
|
assertSame(REF_B, list.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBuilder_Remove() {
|
||||||
|
RefList.Builder<Ref> builder = new RefList.Builder<Ref>();
|
||||||
|
builder.add(REF_A);
|
||||||
|
builder.add(REF_B);
|
||||||
|
builder.remove(0);
|
||||||
|
|
||||||
|
assertEquals(1, builder.size());
|
||||||
|
assertSame(REF_B, builder.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSet() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_A);
|
||||||
|
RefList<Ref> two = one.set(1, REF_B);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified
|
||||||
|
assertEquals(2, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_A, one.get(1));
|
||||||
|
|
||||||
|
// but two is
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_B, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAddToEmptyList() {
|
||||||
|
RefList<Ref> one = toList();
|
||||||
|
RefList<Ref> two = one.add(0, REF_B);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified, but two is
|
||||||
|
assertEquals(0, one.size());
|
||||||
|
assertEquals(1, two.size());
|
||||||
|
assertFalse(two.isEmpty());
|
||||||
|
assertSame(REF_B, two.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAddToFrontOfList() {
|
||||||
|
RefList<Ref> one = toList(REF_A);
|
||||||
|
RefList<Ref> two = one.add(0, REF_B);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified, but two is
|
||||||
|
assertEquals(1, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_B, two.get(0));
|
||||||
|
assertSame(REF_A, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAddToEndOfList() {
|
||||||
|
RefList<Ref> one = toList(REF_A);
|
||||||
|
RefList<Ref> two = one.add(1, REF_B);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified, but two is
|
||||||
|
assertEquals(1, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(REF_B, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAddToMiddleOfListByInsertionPosition() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_c);
|
||||||
|
|
||||||
|
assertEquals(-2, one.find(REF_B.getName()));
|
||||||
|
|
||||||
|
RefList<Ref> two = one.add(one.find(REF_B.getName()), REF_B);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified, but two is
|
||||||
|
assertEquals(2, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_c, one.get(1));
|
||||||
|
|
||||||
|
assertEquals(3, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(REF_B, two.get(1));
|
||||||
|
assertSame(REF_c, two.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPutNewEntry() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_c);
|
||||||
|
RefList<Ref> two = one.put(REF_B);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified, but two is
|
||||||
|
assertEquals(2, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_c, one.get(1));
|
||||||
|
|
||||||
|
assertEquals(3, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(REF_B, two.get(1));
|
||||||
|
assertSame(REF_c, two.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPutReplaceEntry() {
|
||||||
|
Ref otherc = newRef(REF_c.getName());
|
||||||
|
assertNotSame(REF_c, otherc);
|
||||||
|
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_c);
|
||||||
|
RefList<Ref> two = one.put(otherc);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
// one is not modified, but two is
|
||||||
|
assertEquals(2, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_c, one.get(1));
|
||||||
|
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(otherc, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemoveFrontOfList() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_B, REF_c);
|
||||||
|
RefList<Ref> two = one.remove(0);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
assertEquals(3, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_B, one.get(1));
|
||||||
|
assertSame(REF_c, one.get(2));
|
||||||
|
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_B, two.get(0));
|
||||||
|
assertSame(REF_c, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemoveMiddleOfList() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_B, REF_c);
|
||||||
|
RefList<Ref> two = one.remove(1);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
assertEquals(3, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_B, one.get(1));
|
||||||
|
assertSame(REF_c, one.get(2));
|
||||||
|
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(REF_c, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemoveEndOfList() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_B, REF_c);
|
||||||
|
RefList<Ref> two = one.remove(2);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
assertEquals(3, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_B, one.get(1));
|
||||||
|
assertSame(REF_c, one.get(2));
|
||||||
|
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(REF_B, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemoveMakesEmpty() {
|
||||||
|
RefList<Ref> one = toList(REF_A);
|
||||||
|
RefList<Ref> two = one.remove(1);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
assertSame(two, RefList.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString() {
|
||||||
|
StringBuilder exp = new StringBuilder();
|
||||||
|
exp.append("[");
|
||||||
|
exp.append(REF_A);
|
||||||
|
exp.append(", ");
|
||||||
|
exp.append(REF_B);
|
||||||
|
exp.append("]");
|
||||||
|
|
||||||
|
RefList<Ref> list = toList(REF_A, REF_B);
|
||||||
|
assertEquals(exp.toString(), list.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBuilder_ToString() {
|
||||||
|
StringBuilder exp = new StringBuilder();
|
||||||
|
exp.append("[");
|
||||||
|
exp.append(REF_A);
|
||||||
|
exp.append(", ");
|
||||||
|
exp.append(REF_B);
|
||||||
|
exp.append("]");
|
||||||
|
|
||||||
|
RefList.Builder<Ref> list = new RefList.Builder<Ref>();
|
||||||
|
list.add(REF_A);
|
||||||
|
list.add(REF_B);
|
||||||
|
assertEquals(exp.toString(), list.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFindContainsGet() {
|
||||||
|
RefList<Ref> list = toList(REF_A, REF_B, REF_c);
|
||||||
|
|
||||||
|
assertEquals(0, list.find("A"));
|
||||||
|
assertEquals(1, list.find("B"));
|
||||||
|
assertEquals(2, list.find("c"));
|
||||||
|
|
||||||
|
assertEquals(-1, list.find("0"));
|
||||||
|
assertEquals(-2, list.find("AB"));
|
||||||
|
assertEquals(-3, list.find("a"));
|
||||||
|
assertEquals(-4, list.find("z"));
|
||||||
|
|
||||||
|
assertSame(REF_A, list.get("A"));
|
||||||
|
assertSame(REF_B, list.get("B"));
|
||||||
|
assertSame(REF_c, list.get("c"));
|
||||||
|
assertNull(list.get("AB"));
|
||||||
|
assertNull(list.get("z"));
|
||||||
|
|
||||||
|
assertTrue(list.contains("A"));
|
||||||
|
assertTrue(list.contains("B"));
|
||||||
|
assertTrue(list.contains("c"));
|
||||||
|
assertFalse(list.contains("AB"));
|
||||||
|
assertFalse(list.contains("z"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterable() {
|
||||||
|
RefList<Ref> list = toList(REF_A, REF_B, REF_c);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (Ref ref : list)
|
||||||
|
assertSame(list.get(idx++), ref);
|
||||||
|
assertEquals(3, idx);
|
||||||
|
|
||||||
|
Iterator<Ref> i = RefList.emptyList().iterator();
|
||||||
|
try {
|
||||||
|
i.next();
|
||||||
|
fail("did not throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
i = list.iterator();
|
||||||
|
assertTrue(i.hasNext());
|
||||||
|
assertSame(REF_A, i.next());
|
||||||
|
try {
|
||||||
|
i.remove();
|
||||||
|
fail("did not throw UnsupportedOperationException");
|
||||||
|
} catch (UnsupportedOperationException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyLeadingPrefix() {
|
||||||
|
RefList<Ref> one = toList(REF_A, REF_B, REF_c);
|
||||||
|
RefList<Ref> two = one.copy(2).toRefList();
|
||||||
|
assertNotSame(one, two);
|
||||||
|
|
||||||
|
assertEquals(3, one.size());
|
||||||
|
assertSame(REF_A, one.get(0));
|
||||||
|
assertSame(REF_B, one.get(1));
|
||||||
|
assertSame(REF_c, one.get(2));
|
||||||
|
|
||||||
|
assertEquals(2, two.size());
|
||||||
|
assertSame(REF_A, two.get(0));
|
||||||
|
assertSame(REF_B, two.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyConstructorReusesArray() {
|
||||||
|
RefList.Builder<Ref> one = new RefList.Builder<Ref>();
|
||||||
|
one.add(REF_A);
|
||||||
|
|
||||||
|
RefList<Ref> two = new RefList<Ref>(one.toRefList());
|
||||||
|
one.set(0, REF_B);
|
||||||
|
assertSame(REF_B, two.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private RefList<Ref> toList(Ref... refs) {
|
||||||
|
RefList.Builder<Ref> b = new RefList.Builder<Ref>(refs.length);
|
||||||
|
b.addAll(refs, 0, refs.length);
|
||||||
|
return b.toRefList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ref newRef(final String name) {
|
||||||
|
return new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,472 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.util;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.SymbolicRef;
|
||||||
|
|
||||||
|
public class RefMapTest extends TestCase {
|
||||||
|
private static final ObjectId ID_ONE = ObjectId
|
||||||
|
.fromString("41eb0d88f833b558bddeb269b7ab77399cdf98ed");
|
||||||
|
|
||||||
|
private static final ObjectId ID_TWO = ObjectId
|
||||||
|
.fromString("698dd0b8d0c299f080559a1cffc7fe029479a408");
|
||||||
|
|
||||||
|
private RefList<Ref> packed;
|
||||||
|
|
||||||
|
private RefList<Ref> loose;
|
||||||
|
|
||||||
|
private RefList<Ref> resolved;
|
||||||
|
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
packed = RefList.emptyList();
|
||||||
|
loose = RefList.emptyList();
|
||||||
|
resolved = RefList.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmpty_NoPrefix1() {
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertTrue(map.isEmpty()); // before size was computed
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
assertTrue(map.isEmpty()); // after size was computed
|
||||||
|
|
||||||
|
assertFalse(map.entrySet().iterator().hasNext());
|
||||||
|
assertFalse(map.keySet().iterator().hasNext());
|
||||||
|
assertFalse(map.containsKey("a"));
|
||||||
|
assertNull(map.get("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmpty_NoPrefix2() {
|
||||||
|
RefMap map = new RefMap();
|
||||||
|
assertTrue(map.isEmpty()); // before size was computed
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
assertTrue(map.isEmpty()); // after size was computed
|
||||||
|
|
||||||
|
assertFalse(map.entrySet().iterator().hasNext());
|
||||||
|
assertFalse(map.keySet().iterator().hasNext());
|
||||||
|
assertFalse(map.containsKey("a"));
|
||||||
|
assertNull(map.get("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNotEmpty_NoPrefix() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
packed = toList(master);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertFalse(map.isEmpty()); // before size was computed
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
assertFalse(map.isEmpty()); // after size was computed
|
||||||
|
assertSame(master, map.values().iterator().next());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmpty_WithPrefix() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
packed = toList(master);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/tags/", packed, loose, resolved);
|
||||||
|
assertTrue(map.isEmpty()); // before size was computed
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
assertTrue(map.isEmpty()); // after size was computed
|
||||||
|
|
||||||
|
assertFalse(map.entrySet().iterator().hasNext());
|
||||||
|
assertFalse(map.keySet().iterator().hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNotEmpty_WithPrefix() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
packed = toList(master);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/heads/", packed, loose, resolved);
|
||||||
|
assertFalse(map.isEmpty()); // before size was computed
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
assertFalse(map.isEmpty()); // after size was computed
|
||||||
|
assertSame(master, map.values().iterator().next());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testClear() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
loose = toList(master);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertSame(master, map.get("refs/heads/master"));
|
||||||
|
|
||||||
|
map.clear();
|
||||||
|
assertNull(map.get("refs/heads/master"));
|
||||||
|
assertTrue(map.isEmpty());
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterator_RefusesRemove() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
loose = toList(master);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
Iterator<Ref> itr = map.values().iterator();
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(master, itr.next());
|
||||||
|
try {
|
||||||
|
itr.remove();
|
||||||
|
fail("iterator allowed remove");
|
||||||
|
} catch (UnsupportedOperationException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterator_FailsAtEnd() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
loose = toList(master);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
Iterator<Ref> itr = map.values().iterator();
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(master, itr.next());
|
||||||
|
try {
|
||||||
|
itr.next();
|
||||||
|
fail("iterator allowed next");
|
||||||
|
} catch (NoSuchElementException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIterator_MissingUnresolvedSymbolicRefIsBug() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
final Ref headR = newRef("HEAD", master);
|
||||||
|
|
||||||
|
loose = toList(master);
|
||||||
|
// loose should have added newRef("HEAD", "refs/heads/master")
|
||||||
|
resolved = toList(headR);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
Iterator<Ref> itr = map.values().iterator();
|
||||||
|
try {
|
||||||
|
itr.hasNext();
|
||||||
|
fail("iterator did not catch bad input");
|
||||||
|
} catch (IllegalStateException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMerge_HeadMaster() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
final Ref headU = newRef("HEAD", "refs/heads/master");
|
||||||
|
final Ref headR = newRef("HEAD", master);
|
||||||
|
|
||||||
|
loose = toList(headU, master);
|
||||||
|
resolved = toList(headR);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertEquals(2, map.size());
|
||||||
|
assertFalse(map.isEmpty());
|
||||||
|
assertTrue(map.containsKey("refs/heads/master"));
|
||||||
|
assertSame(master, map.get("refs/heads/master"));
|
||||||
|
|
||||||
|
// resolved overrides loose given same name
|
||||||
|
assertSame(headR, map.get("HEAD"));
|
||||||
|
|
||||||
|
Iterator<Ref> itr = map.values().iterator();
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(headR, itr.next());
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(master, itr.next());
|
||||||
|
assertFalse(itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMerge_PackedLooseLoose() {
|
||||||
|
final Ref refA = newRef("A", ID_ONE);
|
||||||
|
final Ref refB_ONE = newRef("B", ID_ONE);
|
||||||
|
final Ref refB_TWO = newRef("B", ID_TWO);
|
||||||
|
final Ref refc = newRef("c", ID_ONE);
|
||||||
|
|
||||||
|
packed = toList(refA, refB_ONE);
|
||||||
|
loose = toList(refB_TWO, refc);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertEquals(3, map.size());
|
||||||
|
assertFalse(map.isEmpty());
|
||||||
|
assertTrue(map.containsKey(refA.getName()));
|
||||||
|
assertSame(refA, map.get(refA.getName()));
|
||||||
|
|
||||||
|
// loose overrides packed given same name
|
||||||
|
assertSame(refB_TWO, map.get(refB_ONE.getName()));
|
||||||
|
|
||||||
|
Iterator<Ref> itr = map.values().iterator();
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(refA, itr.next());
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(refB_TWO, itr.next());
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
assertSame(refc, itr.next());
|
||||||
|
assertFalse(itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMerge_WithPrefix() {
|
||||||
|
final Ref a = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref b = newRef("refs/heads/foo/bar/B", ID_TWO);
|
||||||
|
final Ref c = newRef("refs/heads/foo/rab/C", ID_TWO);
|
||||||
|
final Ref g = newRef("refs/heads/g", ID_ONE);
|
||||||
|
packed = toList(a, b, c, g);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/heads/foo/", packed, loose, resolved);
|
||||||
|
assertEquals(2, map.size());
|
||||||
|
|
||||||
|
assertSame(b, map.get("bar/B"));
|
||||||
|
assertSame(c, map.get("rab/C"));
|
||||||
|
assertNull(map.get("refs/heads/foo/bar/B"));
|
||||||
|
assertNull(map.get("refs/heads/A"));
|
||||||
|
|
||||||
|
assertTrue(map.containsKey("bar/B"));
|
||||||
|
assertTrue(map.containsKey("rab/C"));
|
||||||
|
assertFalse(map.containsKey("refs/heads/foo/bar/B"));
|
||||||
|
assertFalse(map.containsKey("refs/heads/A"));
|
||||||
|
|
||||||
|
Iterator<Map.Entry<String, Ref>> itr = map.entrySet().iterator();
|
||||||
|
Map.Entry<String, Ref> ent;
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
ent = itr.next();
|
||||||
|
assertEquals("bar/B", ent.getKey());
|
||||||
|
assertSame(b, ent.getValue());
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
ent = itr.next();
|
||||||
|
assertEquals("rab/C", ent.getKey());
|
||||||
|
assertSame(c, ent.getValue());
|
||||||
|
assertFalse(itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPut_KeyMustMatchName_NoPrefix() {
|
||||||
|
final Ref refA = newRef("refs/heads/A", ID_ONE);
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
try {
|
||||||
|
map.put("FOO", refA);
|
||||||
|
fail("map accepted invalid key/value pair");
|
||||||
|
} catch (IllegalArgumentException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPut_KeyMustMatchName_WithPrefix() {
|
||||||
|
final Ref refA = newRef("refs/heads/A", ID_ONE);
|
||||||
|
RefMap map = new RefMap("refs/heads/", packed, loose, resolved);
|
||||||
|
try {
|
||||||
|
map.put("FOO", refA);
|
||||||
|
fail("map accepted invalid key/value pair");
|
||||||
|
} catch (IllegalArgumentException err) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPut_NoPrefix() {
|
||||||
|
final Ref refA_one = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref refA_two = newRef("refs/heads/A", ID_TWO);
|
||||||
|
|
||||||
|
packed = toList(refA_one);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertSame(refA_one, map.get(refA_one.getName()));
|
||||||
|
assertSame(refA_one, map.put(refA_one.getName(), refA_two));
|
||||||
|
|
||||||
|
// map changed, but packed, loose did not
|
||||||
|
assertSame(refA_two, map.get(refA_one.getName()));
|
||||||
|
assertSame(refA_one, packed.get(0));
|
||||||
|
assertEquals(0, loose.size());
|
||||||
|
|
||||||
|
assertSame(refA_two, map.put(refA_one.getName(), refA_one));
|
||||||
|
assertSame(refA_one, map.get(refA_one.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPut_WithPrefix() {
|
||||||
|
final Ref refA_one = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref refA_two = newRef("refs/heads/A", ID_TWO);
|
||||||
|
|
||||||
|
packed = toList(refA_one);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/heads/", packed, loose, resolved);
|
||||||
|
assertSame(refA_one, map.get("A"));
|
||||||
|
assertSame(refA_one, map.put("A", refA_two));
|
||||||
|
|
||||||
|
// map changed, but packed, loose did not
|
||||||
|
assertSame(refA_two, map.get("A"));
|
||||||
|
assertSame(refA_one, packed.get(0));
|
||||||
|
assertEquals(0, loose.size());
|
||||||
|
|
||||||
|
assertSame(refA_two, map.put("A", refA_one));
|
||||||
|
assertSame(refA_one, map.get("A"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPut_CollapseResolved() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
final Ref headU = newRef("HEAD", "refs/heads/master");
|
||||||
|
final Ref headR = newRef("HEAD", master);
|
||||||
|
final Ref a = newRef("refs/heads/A", ID_ONE);
|
||||||
|
|
||||||
|
loose = toList(headU, master);
|
||||||
|
resolved = toList(headR);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertNull(map.put(a.getName(), a));
|
||||||
|
assertSame(a, map.get(a.getName()));
|
||||||
|
assertSame(headR, map.get("HEAD"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRemove() {
|
||||||
|
final Ref master = newRef("refs/heads/master", ID_ONE);
|
||||||
|
final Ref headU = newRef("HEAD", "refs/heads/master");
|
||||||
|
final Ref headR = newRef("HEAD", master);
|
||||||
|
|
||||||
|
packed = toList(master);
|
||||||
|
loose = toList(headU, master);
|
||||||
|
resolved = toList(headR);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertNull(map.remove("not.a.reference"));
|
||||||
|
|
||||||
|
assertSame(master, map.remove("refs/heads/master"));
|
||||||
|
assertNull(map.get("refs/heads/master"));
|
||||||
|
|
||||||
|
assertSame(headR, map.remove("HEAD"));
|
||||||
|
assertNull(map.get("HEAD"));
|
||||||
|
|
||||||
|
assertTrue(map.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_NoPrefix() {
|
||||||
|
final Ref a = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref b = newRef("refs/heads/B", ID_TWO);
|
||||||
|
|
||||||
|
packed = toList(a, b);
|
||||||
|
|
||||||
|
StringBuilder exp = new StringBuilder();
|
||||||
|
exp.append("[");
|
||||||
|
exp.append(a.toString());
|
||||||
|
exp.append(", ");
|
||||||
|
exp.append(b.toString());
|
||||||
|
exp.append("]");
|
||||||
|
|
||||||
|
RefMap map = new RefMap("", packed, loose, resolved);
|
||||||
|
assertEquals(exp.toString(), map.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToString_WithPrefix() {
|
||||||
|
final Ref a = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref b = newRef("refs/heads/foo/B", ID_TWO);
|
||||||
|
final Ref c = newRef("refs/heads/foo/C", ID_TWO);
|
||||||
|
final Ref g = newRef("refs/heads/g", ID_ONE);
|
||||||
|
|
||||||
|
packed = toList(a, b, c, g);
|
||||||
|
|
||||||
|
StringBuilder exp = new StringBuilder();
|
||||||
|
exp.append("[");
|
||||||
|
exp.append(b.toString());
|
||||||
|
exp.append(", ");
|
||||||
|
exp.append(c.toString());
|
||||||
|
exp.append("]");
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/heads/foo/", packed, loose, resolved);
|
||||||
|
assertEquals(exp.toString(), map.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEntryType() {
|
||||||
|
final Ref a = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref b = newRef("refs/heads/B", ID_TWO);
|
||||||
|
|
||||||
|
packed = toList(a, b);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/heads/", packed, loose, resolved);
|
||||||
|
Iterator<Map.Entry<String, Ref>> itr = map.entrySet().iterator();
|
||||||
|
Map.Entry<String, Ref> ent_a = itr.next();
|
||||||
|
Map.Entry<String, Ref> ent_b = itr.next();
|
||||||
|
|
||||||
|
assertEquals(ent_a.hashCode(), "A".hashCode());
|
||||||
|
assertTrue(ent_a.equals(ent_a));
|
||||||
|
assertFalse(ent_a.equals(ent_b));
|
||||||
|
|
||||||
|
assertEquals(a.toString(), ent_a.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEntryTypeSet() {
|
||||||
|
final Ref refA_one = newRef("refs/heads/A", ID_ONE);
|
||||||
|
final Ref refA_two = newRef("refs/heads/A", ID_TWO);
|
||||||
|
|
||||||
|
packed = toList(refA_one);
|
||||||
|
|
||||||
|
RefMap map = new RefMap("refs/heads/", packed, loose, resolved);
|
||||||
|
assertSame(refA_one, map.get("A"));
|
||||||
|
|
||||||
|
Map.Entry<String, Ref> ent = map.entrySet().iterator().next();
|
||||||
|
assertEquals("A", ent.getKey());
|
||||||
|
assertSame(refA_one, ent.getValue());
|
||||||
|
|
||||||
|
assertSame(refA_one, ent.setValue(refA_two));
|
||||||
|
assertSame(refA_two, ent.getValue());
|
||||||
|
assertSame(refA_two, map.get("A"));
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private RefList<Ref> toList(Ref... refs) {
|
||||||
|
RefList.Builder<Ref> b = new RefList.Builder<Ref>(refs.length);
|
||||||
|
b.addAll(refs, 0, refs.length);
|
||||||
|
return b.toRefList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ref newRef(String name, String dst) {
|
||||||
|
return newRef(name,
|
||||||
|
new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ref newRef(String name, Ref dst) {
|
||||||
|
return new SymbolicRef(name, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ref newRef(String name, ObjectId id) {
|
||||||
|
return new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
@ -146,7 +147,7 @@ void paintTriangleDown(final int cx, final int y, final int h) {
|
||||||
@Override
|
@Override
|
||||||
protected int drawLabel(int x, int y, Ref ref) {
|
protected int drawLabel(int x, int y, Ref ref) {
|
||||||
String txt;
|
String txt;
|
||||||
String name = ref.getOrigName();
|
String name = ref.getName();
|
||||||
if (name.startsWith(Constants.R_HEADS)) {
|
if (name.startsWith(Constants.R_HEADS)) {
|
||||||
g.setBackground(Color.GREEN);
|
g.setBackground(Color.GREEN);
|
||||||
txt = name.substring(Constants.R_HEADS.length());
|
txt = name.substring(Constants.R_HEADS.length());
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.channels.FileLock;
|
import java.nio.channels.FileLock;
|
||||||
|
@ -65,6 +66,15 @@
|
||||||
* name.
|
* name.
|
||||||
*/
|
*/
|
||||||
public class LockFile {
|
public class LockFile {
|
||||||
|
static final String SUFFIX = ".lock"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
/** Filter to skip over active lock files when listing a directory. */
|
||||||
|
static final FilenameFilter FILTER = new FilenameFilter() {
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return !name.endsWith(SUFFIX);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final File ref;
|
private final File ref;
|
||||||
|
|
||||||
private final File lck;
|
private final File lck;
|
||||||
|
@ -87,7 +97,7 @@ public class LockFile {
|
||||||
*/
|
*/
|
||||||
public LockFile(final File f) {
|
public LockFile(final File f) {
|
||||||
ref = f;
|
ref = f;
|
||||||
lck = new File(ref.getParentFile(), ref.getName() + ".lock");
|
lck = new File(ref.getParentFile(), ref.getName() + SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -334,6 +344,30 @@ public void setNeedStatInformation(final boolean on) {
|
||||||
needStatInformation = on;
|
needStatInformation = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the lock file information differs from the old file.
|
||||||
|
* <p>
|
||||||
|
* This method tests both the length and the last modification date. If both
|
||||||
|
* are the same, this method sleeps until it can force the new lock file's
|
||||||
|
* modification date to be later than the target file.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
* the thread was interrupted before the last modified date of
|
||||||
|
* the lock file was different from the last modified date of
|
||||||
|
* the target file.
|
||||||
|
*/
|
||||||
|
public void waitForStatChange() throws InterruptedException {
|
||||||
|
if (ref.length() == lck.length()) {
|
||||||
|
long otime = ref.lastModified();
|
||||||
|
long ntime = lck.lastModified();
|
||||||
|
while (otime == ntime) {
|
||||||
|
Thread.sleep(25 /* milliseconds */);
|
||||||
|
lck.setLastModified(System.currentTimeMillis());
|
||||||
|
ntime = lck.lastModified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commit this change and release the lock.
|
* Commit this change and release the lock.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
|
/** A {@link Ref} that points directly at an {@link ObjectId}. */
|
||||||
|
public abstract class ObjectIdRef implements Ref {
|
||||||
|
/** Any reference whose peeled value is not yet known. */
|
||||||
|
public static class Unpeeled extends ObjectIdRef {
|
||||||
|
/**
|
||||||
|
* Create a new ref pairing.
|
||||||
|
*
|
||||||
|
* @param st
|
||||||
|
* method used to store this ref.
|
||||||
|
* @param name
|
||||||
|
* name of this ref.
|
||||||
|
* @param id
|
||||||
|
* current value of the ref. May be null to indicate a ref
|
||||||
|
* that does not exist yet.
|
||||||
|
*/
|
||||||
|
public Unpeeled(Storage st, String name, ObjectId id) {
|
||||||
|
super(st, name, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectId getPeeledObjectId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPeeled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An annotated tag whose peeled object has been cached. */
|
||||||
|
public static class PeeledTag extends ObjectIdRef {
|
||||||
|
private final ObjectId peeledObjectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ref pairing.
|
||||||
|
*
|
||||||
|
* @param st
|
||||||
|
* method used to store this ref.
|
||||||
|
* @param name
|
||||||
|
* name of this ref.
|
||||||
|
* @param id
|
||||||
|
* current value of the ref.
|
||||||
|
* @param p
|
||||||
|
* the first non-tag object that tag {@code id} points to.
|
||||||
|
*/
|
||||||
|
public PeeledTag(Storage st, String name, ObjectId id, ObjectId p) {
|
||||||
|
super(st, name, id);
|
||||||
|
peeledObjectId = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectId getPeeledObjectId() {
|
||||||
|
return peeledObjectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPeeled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A reference to a non-tag object coming from a cached source. */
|
||||||
|
public static class PeeledNonTag extends ObjectIdRef {
|
||||||
|
/**
|
||||||
|
* Create a new ref pairing.
|
||||||
|
*
|
||||||
|
* @param st
|
||||||
|
* method used to store this ref.
|
||||||
|
* @param name
|
||||||
|
* name of this ref.
|
||||||
|
* @param id
|
||||||
|
* current value of the ref. May be null to indicate a ref
|
||||||
|
* that does not exist yet.
|
||||||
|
*/
|
||||||
|
public PeeledNonTag(Storage st, String name, ObjectId id) {
|
||||||
|
super(st, name, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectId getPeeledObjectId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPeeled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final Storage storage;
|
||||||
|
|
||||||
|
private final ObjectId objectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ref pairing.
|
||||||
|
*
|
||||||
|
* @param st
|
||||||
|
* method used to store this ref.
|
||||||
|
* @param name
|
||||||
|
* name of this ref.
|
||||||
|
* @param id
|
||||||
|
* current value of the ref. May be null to indicate a ref that
|
||||||
|
* does not exist yet.
|
||||||
|
*/
|
||||||
|
protected ObjectIdRef(Storage st, String name, ObjectId id) {
|
||||||
|
this.name = name;
|
||||||
|
this.storage = st;
|
||||||
|
this.objectId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSymbolic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref getLeaf() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref getTarget() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectId getObjectId() {
|
||||||
|
return objectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Storage getStorage() {
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder r = new StringBuilder();
|
||||||
|
r.append("Ref[");
|
||||||
|
r.append(getName());
|
||||||
|
r.append('=');
|
||||||
|
r.append(ObjectId.toString(getObjectId()));
|
||||||
|
r.append(']');
|
||||||
|
return r.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,12 +50,12 @@
|
||||||
* identifier. The object identifier can be any valid Git object (blob, tree,
|
* identifier. The object identifier can be any valid Git object (blob, tree,
|
||||||
* commit, annotated tag, ...).
|
* commit, annotated tag, ...).
|
||||||
* <p>
|
* <p>
|
||||||
* The ref name has the attributes of the ref that was asked for as well as
|
* The ref name has the attributes of the ref that was asked for as well as the
|
||||||
* the ref it was resolved to for symbolic refs plus the object id it points
|
* ref it was resolved to for symbolic refs plus the object id it points to and
|
||||||
* to and (for tags) the peeled target object id, i.e. the tag resolved
|
* (for tags) the peeled target object id, i.e. the tag resolved recursively
|
||||||
* recursively until a non-tag object is referenced.
|
* until a non-tag object is referenced.
|
||||||
*/
|
*/
|
||||||
public class Ref {
|
public interface Ref {
|
||||||
/** Location where a {@link Ref} is stored. */
|
/** Location where a {@link Ref} is stored. */
|
||||||
public static enum Storage {
|
public static enum Storage {
|
||||||
/**
|
/**
|
||||||
|
@ -73,8 +73,7 @@ public static enum Storage {
|
||||||
LOOSE(true, false),
|
LOOSE(true, false),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ref is stored in the <code>packed-refs</code> file, with
|
* The ref is stored in the <code>packed-refs</code> file, with others.
|
||||||
* others.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Updating this ref requires rewriting the file, with perhaps many
|
* Updating this ref requires rewriting the file, with perhaps many
|
||||||
* other refs being included at the same time.
|
* other refs being included at the same time.
|
||||||
|
@ -122,123 +121,63 @@ public boolean isPacked() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Storage storage;
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private ObjectId objectId;
|
|
||||||
|
|
||||||
private ObjectId peeledObjectId;
|
|
||||||
|
|
||||||
private final String origName;
|
|
||||||
|
|
||||||
private final boolean peeled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ref pairing.
|
|
||||||
*
|
|
||||||
* @param st
|
|
||||||
* method used to store this ref.
|
|
||||||
* @param origName
|
|
||||||
* The name used to resolve this ref
|
|
||||||
* @param refName
|
|
||||||
* name of this ref.
|
|
||||||
* @param id
|
|
||||||
* current value of the ref. May be null to indicate a ref that
|
|
||||||
* does not exist yet.
|
|
||||||
*/
|
|
||||||
public Ref(final Storage st, final String origName, final String refName, final ObjectId id) {
|
|
||||||
this(st, origName, refName, id, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ref pairing.
|
|
||||||
*
|
|
||||||
* @param st
|
|
||||||
* method used to store this ref.
|
|
||||||
* @param refName
|
|
||||||
* name of this ref.
|
|
||||||
* @param id
|
|
||||||
* current value of the ref. May be null to indicate a ref that
|
|
||||||
* does not exist yet.
|
|
||||||
*/
|
|
||||||
public Ref(final Storage st, final String refName, final ObjectId id) {
|
|
||||||
this(st, refName, refName, id, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ref pairing.
|
|
||||||
*
|
|
||||||
* @param st
|
|
||||||
* method used to store this ref.
|
|
||||||
* @param origName
|
|
||||||
* The name used to resolve this ref
|
|
||||||
* @param refName
|
|
||||||
* name of this ref.
|
|
||||||
* @param id
|
|
||||||
* current value of the ref. May be null to indicate a ref that
|
|
||||||
* does not exist yet.
|
|
||||||
* @param peel
|
|
||||||
* peeled value of the ref's tag. May be null if this is not a
|
|
||||||
* tag or not yet peeled (in which case the next parameter should be null)
|
|
||||||
* @param peeled
|
|
||||||
* true if peel represents a the peeled value of the object
|
|
||||||
*/
|
|
||||||
public Ref(final Storage st, final String origName, final String refName, final ObjectId id,
|
|
||||||
final ObjectId peel, final boolean peeled) {
|
|
||||||
storage = st;
|
|
||||||
this.origName = origName;
|
|
||||||
name = refName;
|
|
||||||
objectId = id;
|
|
||||||
peeledObjectId = peel;
|
|
||||||
this.peeled = peeled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ref pairing.
|
|
||||||
*
|
|
||||||
* @param st
|
|
||||||
* method used to store this ref.
|
|
||||||
* @param refName
|
|
||||||
* name of this ref.
|
|
||||||
* @param id
|
|
||||||
* current value of the ref. May be null to indicate a ref that
|
|
||||||
* does not exist yet.
|
|
||||||
* @param peel
|
|
||||||
* peeled value of the ref's tag. May be null if this is not a
|
|
||||||
* tag or the peeled value is not known.
|
|
||||||
* @param peeled
|
|
||||||
* true if peel represents a the peeled value of the object
|
|
||||||
*/
|
|
||||||
public Ref(final Storage st, final String refName, final ObjectId id,
|
|
||||||
final ObjectId peel, boolean peeled) {
|
|
||||||
this(st, refName, refName, id, peel, peeled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What this ref is called within the repository.
|
* What this ref is called within the repository.
|
||||||
*
|
*
|
||||||
* @return name of this ref.
|
* @return name of this ref.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName();
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the originally resolved name
|
* Test if this reference is a symbolic reference.
|
||||||
|
* <p>
|
||||||
|
* A symbolic reference does not have its own {@link ObjectId} value, but
|
||||||
|
* instead points to another {@code Ref} in the same database and always
|
||||||
|
* uses that other reference's value as its own.
|
||||||
|
*
|
||||||
|
* @return true if this is a symbolic reference; false if this reference
|
||||||
|
* contains its own ObjectId.
|
||||||
*/
|
*/
|
||||||
public String getOrigName() {
|
public abstract boolean isSymbolic();
|
||||||
return origName;
|
|
||||||
}
|
/**
|
||||||
|
* Traverse target references until {@link #isSymbolic()} is false.
|
||||||
|
* <p>
|
||||||
|
* If {@link #isSymbolic()} is false, returns {@code this}.
|
||||||
|
* <p>
|
||||||
|
* If {@link #isSymbolic()} is true, this method recursively traverses
|
||||||
|
* {@link #getTarget()} until {@link #isSymbolic()} returns false.
|
||||||
|
* <p>
|
||||||
|
* This method is effectively
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* return isSymbolic() ? getTarget().getLeaf() : this;
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return the reference that actually stores the ObjectId value.
|
||||||
|
*/
|
||||||
|
public abstract Ref getLeaf();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the reference this reference points to, or {@code this}.
|
||||||
|
* <p>
|
||||||
|
* If {@link #isSymbolic()} is true this method returns the reference it
|
||||||
|
* directly names, which might not be the leaf reference, but could be
|
||||||
|
* another symbolic reference.
|
||||||
|
* <p>
|
||||||
|
* If this is a leaf level reference that contains its own ObjectId,this
|
||||||
|
* method returns {@code this}.
|
||||||
|
*
|
||||||
|
* @return the target reference, or {@code this}.
|
||||||
|
*/
|
||||||
|
public abstract Ref getTarget();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached value of this ref.
|
* Cached value of this ref.
|
||||||
*
|
*
|
||||||
* @return the value of this ref at the last time we read it.
|
* @return the value of this ref at the last time we read it.
|
||||||
*/
|
*/
|
||||||
public ObjectId getObjectId() {
|
public abstract ObjectId getObjectId();
|
||||||
return objectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached value of <code>ref^{}</code> (the ref peeled to commit).
|
* Cached value of <code>ref^{}</code> (the ref peeled to commit).
|
||||||
|
@ -247,18 +186,12 @@ public ObjectId getObjectId() {
|
||||||
* blob) that the annotated tag refers to; null if this ref does not
|
* blob) that the annotated tag refers to; null if this ref does not
|
||||||
* refer to an annotated tag.
|
* refer to an annotated tag.
|
||||||
*/
|
*/
|
||||||
public ObjectId getPeeledObjectId() {
|
public abstract ObjectId getPeeledObjectId();
|
||||||
if (!peeled)
|
|
||||||
return null;
|
|
||||||
return peeledObjectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether the Ref represents a peeled tag
|
* @return whether the Ref represents a peeled tag
|
||||||
*/
|
*/
|
||||||
public boolean isPeeled() {
|
public abstract boolean isPeeled();
|
||||||
return peeled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How was this ref obtained?
|
* How was this ref obtained?
|
||||||
|
@ -268,18 +201,5 @@ public boolean isPeeled() {
|
||||||
*
|
*
|
||||||
* @return type of ref.
|
* @return type of ref.
|
||||||
*/
|
*/
|
||||||
public Storage getStorage() {
|
public abstract Storage getStorage();
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
String o = "";
|
|
||||||
if (!origName.equals(name))
|
|
||||||
o = "(" + origName + ")";
|
|
||||||
return "Ref[" + o + name + "=" + ObjectId.toString(getObjectId()) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPeeledObjectId(final ObjectId id) {
|
|
||||||
peeledObjectId = id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
|
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
|
||||||
* Copyright (C) 2008, Google Inc.
|
* Copyright (C) 2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -61,12 +61,12 @@ public class RefComparator implements Comparator<Ref> {
|
||||||
public static final RefComparator INSTANCE = new RefComparator();
|
public static final RefComparator INSTANCE = new RefComparator();
|
||||||
|
|
||||||
public int compare(final Ref o1, final Ref o2) {
|
public int compare(final Ref o1, final Ref o2) {
|
||||||
return o1.getOrigName().compareTo(o2.getOrigName());
|
return compareTo(o1, o2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the collection of refs, returning a new collection.
|
* Sorts the collection of refs, returning a new collection.
|
||||||
*
|
*
|
||||||
* @param refs
|
* @param refs
|
||||||
* collection to be sorted
|
* collection to be sorted
|
||||||
* @return sorted collection of refs
|
* @return sorted collection of refs
|
||||||
|
@ -76,4 +76,30 @@ public static Collection<Ref> sort(final Collection<Ref> refs) {
|
||||||
Collections.sort(r, INSTANCE);
|
Collections.sort(r, INSTANCE);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare a reference to a name.
|
||||||
|
*
|
||||||
|
* @param o1
|
||||||
|
* the reference instance.
|
||||||
|
* @param o2
|
||||||
|
* the name to compare to.
|
||||||
|
* @return standard Comparator result of < 0, 0, > 0.
|
||||||
|
*/
|
||||||
|
public static int compareTo(Ref o1, String o2) {
|
||||||
|
return o1.getName().compareTo(o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two references by name.
|
||||||
|
*
|
||||||
|
* @param o1
|
||||||
|
* the reference instance.
|
||||||
|
* @param o2
|
||||||
|
* the other reference instance.
|
||||||
|
* @return standard Comparator result of < 0, 0, > 0.
|
||||||
|
*/
|
||||||
|
public static int compareTo(final Ref o1, final Ref o2) {
|
||||||
|
return o1.getName().compareTo(o2.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2010, Google Inc.
|
||||||
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
|
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -44,499 +43,156 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.lib;
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.ObjectWritingException;
|
/**
|
||||||
import org.eclipse.jgit.lib.Ref.Storage;
|
* Abstraction of name to {@link ObjectId} mapping.
|
||||||
import org.eclipse.jgit.util.FS;
|
* <p>
|
||||||
import org.eclipse.jgit.util.IO;
|
* A reference database stores a mapping of reference names to {@link ObjectId}.
|
||||||
import org.eclipse.jgit.util.RawParseUtils;
|
* Every {@link Repository} has a single reference database, mapping names to
|
||||||
|
* the tips of the object graph contained by the {@link ObjectDatabase}.
|
||||||
class RefDatabase {
|
*/
|
||||||
private static final String REFS_SLASH = "refs/";
|
public abstract class RefDatabase {
|
||||||
|
|
||||||
private static final String[] refSearchPaths = { "", REFS_SLASH,
|
|
||||||
R_TAGS, Constants.R_HEADS, Constants.R_REMOTES };
|
|
||||||
|
|
||||||
private final Repository db;
|
|
||||||
|
|
||||||
private final File gitDir;
|
|
||||||
|
|
||||||
private final File refsDir;
|
|
||||||
|
|
||||||
private Map<String, Ref> looseRefs;
|
|
||||||
private Map<String, Long> looseRefsMTime;
|
|
||||||
private Map<String, String> looseSymRefs;
|
|
||||||
|
|
||||||
private final File packedRefsFile;
|
|
||||||
|
|
||||||
private Map<String, Ref> packedRefs;
|
|
||||||
|
|
||||||
private long packedRefsLastModified;
|
|
||||||
|
|
||||||
private long packedRefsLength;
|
|
||||||
|
|
||||||
int lastRefModification;
|
|
||||||
|
|
||||||
int lastNotifiedRefModification;
|
|
||||||
|
|
||||||
private int refModificationCounter;
|
|
||||||
|
|
||||||
RefDatabase(final Repository r) {
|
|
||||||
db = r;
|
|
||||||
gitDir = db.getDirectory();
|
|
||||||
refsDir = FS.resolve(gitDir, "refs");
|
|
||||||
packedRefsFile = FS.resolve(gitDir, Constants.PACKED_REFS);
|
|
||||||
clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void clearCache() {
|
|
||||||
looseRefs = new HashMap<String, Ref>();
|
|
||||||
looseRefsMTime = new HashMap<String, Long>();
|
|
||||||
packedRefs = new HashMap<String, Ref>();
|
|
||||||
looseSymRefs = new HashMap<String, String>();
|
|
||||||
packedRefsLastModified = 0;
|
|
||||||
packedRefsLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Repository getRepository() {
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
|
|
||||||
void create() {
|
|
||||||
refsDir.mkdir();
|
|
||||||
new File(refsDir, "heads").mkdir();
|
|
||||||
new File(refsDir, "tags").mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectId idOf(final String name) throws IOException {
|
|
||||||
refreshPackedRefs();
|
|
||||||
final Ref r = readRefBasic(name, 0);
|
|
||||||
return r != null ? r.getObjectId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a command to update, create or delete a ref in this repository.
|
* Order of prefixes to search when using non-absolute references.
|
||||||
*
|
* <p>
|
||||||
* @param name
|
* The implementation's {@link #getRef(String)} method must take this search
|
||||||
* name of the ref the caller wants to modify.
|
* space into consideration when locating a reference by name. The first
|
||||||
* @return an update command. The caller must finish populating this command
|
* entry in the path is always {@code ""}, ensuring that absolute references
|
||||||
* and then invoke one of the update methods to actually make a
|
* are resolved without further mangling.
|
||||||
* change.
|
|
||||||
* @throws IOException
|
|
||||||
* a symbolic ref was passed in and could not be resolved back
|
|
||||||
* to the base ref, as the symbolic ref could not be read.
|
|
||||||
*/
|
*/
|
||||||
RefUpdate newUpdate(final String name) throws IOException {
|
protected static final String[] SEARCH_PATH = { "", //$NON-NLS-1$
|
||||||
return newUpdate(name, false);
|
Constants.R_REFS, //
|
||||||
}
|
Constants.R_TAGS, //
|
||||||
|
Constants.R_HEADS, //
|
||||||
|
Constants.R_REMOTES //
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a command to update, create or delete a ref in this repository.
|
* Maximum number of times a {@link SymbolicRef} can be traversed.
|
||||||
|
* <p>
|
||||||
|
* If the reference is nested deeper than this depth, the implementation
|
||||||
|
* should either fail, or at least claim the reference does not exist.
|
||||||
|
*/
|
||||||
|
protected static final int MAX_SYMBOLIC_REF_DEPTH = 5;
|
||||||
|
|
||||||
|
/** Magic value for {@link #getRefs(String)} to return all references. */
|
||||||
|
public static final String ALL = "";//$NON-NLS-1$
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new reference database at this location.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* the database could not be created.
|
||||||
|
*/
|
||||||
|
public abstract void create() throws IOException;
|
||||||
|
|
||||||
|
/** Close any resources held by this database. */
|
||||||
|
public abstract void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a proposed reference name overlaps with an existing one.
|
||||||
|
* <p>
|
||||||
|
* Reference names use '/' as a component separator, and may be stored in a
|
||||||
|
* hierarchical storage such as a directory on the local filesystem.
|
||||||
|
* <p>
|
||||||
|
* If the reference "refs/heads/foo" exists then "refs/heads/foo/bar" must
|
||||||
|
* not exist, as a reference cannot have a value and also be a container for
|
||||||
|
* other references at the same time.
|
||||||
|
* <p>
|
||||||
|
* If the reference "refs/heads/foo/bar" exists than the reference
|
||||||
|
* "refs/heads/foo" cannot exist, for the same reason.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* name of the ref the caller wants to modify.
|
* proposed name.
|
||||||
|
* @return true if the name overlaps with an existing reference; false if
|
||||||
|
* using this name right now would be safe.
|
||||||
|
* @throws IOException
|
||||||
|
* the database could not be read to check for conflicts.
|
||||||
|
*/
|
||||||
|
public abstract boolean isNameConflicting(String name) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new update command to create, modify or delete a reference.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the reference.
|
||||||
* @param detach
|
* @param detach
|
||||||
* true to detach the ref, i.e. replace symref with object ref
|
* if {@code true} and {@code name} is currently a
|
||||||
* @return an update command. The caller must finish populating this command
|
* {@link SymbolicRef}, the update will replace it with an
|
||||||
* and then invoke one of the update methods to actually make a
|
* {@link ObjectIdRef}. Otherwise, the update will recursively
|
||||||
* change.
|
* traverse {@link SymbolicRef}s and operate on the leaf
|
||||||
|
* {@link ObjectIdRef}.
|
||||||
|
* @return a new update for the requested name; never null.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* a symbolic ref was passed in and could not be resolved back
|
* the reference space cannot be accessed.
|
||||||
* to the base ref, as the symbolic ref could not be read.
|
|
||||||
*/
|
*/
|
||||||
RefUpdate newUpdate(final String name, boolean detach) throws IOException {
|
public abstract RefUpdate newUpdate(String name, boolean detach)
|
||||||
refreshPackedRefs();
|
throws IOException;
|
||||||
Ref r = readRefBasic(name, 0);
|
|
||||||
if (r == null)
|
|
||||||
r = new Ref(Ref.Storage.NEW, name, null);
|
|
||||||
else if (detach)
|
|
||||||
r = new Ref(Ref.Storage.NEW, name, r.getObjectId());
|
|
||||||
return new RefUpdate(this, r, fileForRef(r.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void stored(final String origName, final String name, final ObjectId id, final long time) {
|
|
||||||
synchronized (this) {
|
|
||||||
looseRefs.put(name, new Ref(Ref.Storage.LOOSE, name, name, id));
|
|
||||||
looseRefsMTime.put(name, time);
|
|
||||||
setModified();
|
|
||||||
}
|
|
||||||
db.fireRefsMaybeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An set of update operations for renaming a ref
|
* Create a new update command to rename a reference.
|
||||||
*
|
*
|
||||||
* @param fromRef Old ref name
|
* @param fromName
|
||||||
* @param toRef New ref name
|
* name of reference to rename from
|
||||||
* @return a RefUpdate operation to rename a ref
|
* @param toName
|
||||||
|
* name of reference to rename to
|
||||||
|
* @return an update command that knows how to rename a branch to another.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
* the reference space cannot be accessed.
|
||||||
*/
|
*/
|
||||||
RefRename newRename(String fromRef, String toRef) throws IOException {
|
public abstract RefRename newRename(String fromName, String toName)
|
||||||
refreshPackedRefs();
|
throws IOException;
|
||||||
Ref f = readRefBasic(fromRef, 0);
|
|
||||||
Ref t = new Ref(Ref.Storage.NEW, toRef, null);
|
|
||||||
RefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));
|
|
||||||
RefUpdate refUpdateTo = new RefUpdate(this, t, fileForRef(t.getName()));
|
|
||||||
return new RefRename(refUpdateTo, refUpdateFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a symref (e.g. HEAD) to disk
|
* Read a single reference.
|
||||||
|
* <p>
|
||||||
|
* Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
|
||||||
|
* able to more quickly resolve a single reference name than obtaining the
|
||||||
|
* complete namespace by {@code getRefs(ALL).get(name)}.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* symref name
|
* the name of the reference. May be a short name which must be
|
||||||
* @param target
|
* searched for using the standard {@link #SEARCH_PATH}.
|
||||||
* pointed to ref
|
* @return the reference (if it exists); else {@code null}.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
* the reference space cannot be accessed.
|
||||||
*/
|
*/
|
||||||
void link(final String name, final String target) throws IOException {
|
public abstract Ref getRef(String name) throws IOException;
|
||||||
final byte[] content = Constants.encode("ref: " + target + "\n");
|
|
||||||
lockAndWriteFile(fileForRef(name), content);
|
|
||||||
synchronized (this) {
|
|
||||||
looseSymRefs.remove(name);
|
|
||||||
setModified();
|
|
||||||
}
|
|
||||||
db.fireRefsMaybeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void uncacheSymRef(String name) {
|
|
||||||
synchronized(this) {
|
|
||||||
looseSymRefs.remove(name);
|
|
||||||
setModified();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void uncacheRef(String name) {
|
|
||||||
looseRefs.remove(name);
|
|
||||||
looseRefsMTime.remove(name);
|
|
||||||
packedRefs.remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setModified() {
|
|
||||||
lastRefModification = refModificationCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref readRef(final String partialName) throws IOException {
|
|
||||||
refreshPackedRefs();
|
|
||||||
for (int k = 0; k < refSearchPaths.length; k++) {
|
|
||||||
final Ref r = readRefBasic(refSearchPaths[k] + partialName, 0);
|
|
||||||
if (r != null && r.getObjectId() != null)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all known refs (heads, tags, remotes).
|
* Get a section of the reference namespace.
|
||||||
|
*
|
||||||
|
* @param prefix
|
||||||
|
* prefix to search the namespace with; must end with {@code /}.
|
||||||
|
* If the empty string ({@link #ALL}), obtain a complete snapshot
|
||||||
|
* of all references.
|
||||||
|
* @return modifiable map that is a complete snapshot of the current
|
||||||
|
* reference namespace, with {@code prefix} removed from the start
|
||||||
|
* of each key. The map can be an unsorted map.
|
||||||
|
* @throws IOException
|
||||||
|
* the reference space cannot be accessed.
|
||||||
*/
|
*/
|
||||||
Map<String, Ref> getAllRefs() {
|
public abstract Map<String, Ref> getRefs(String prefix) throws IOException;
|
||||||
return readRefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all tags; key is short tag name ("v1.0") and value of the entry
|
* Peel a possibly unpeeled reference by traversing the annotated tags.
|
||||||
* contains the ref with the full tag name ("refs/tags/v1.0").
|
* <p>
|
||||||
|
* If the reference cannot be peeled (as it does not refer to an annotated
|
||||||
|
* tag) the peeled id stays null, but {@link Ref#isPeeled()} will be true.
|
||||||
|
* <p>
|
||||||
|
* Implementors should check {@link Ref#isPeeled()} before performing any
|
||||||
|
* additional work effort.
|
||||||
|
*
|
||||||
|
* @param ref
|
||||||
|
* The reference to peel
|
||||||
|
* @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new
|
||||||
|
* Ref object representing the same data as Ref, but isPeeled() will
|
||||||
|
* be true and getPeeledObjectId() will contain the peeled object
|
||||||
|
* (or null).
|
||||||
|
* @throws IOException
|
||||||
|
* the reference space or object space cannot be accessed.
|
||||||
*/
|
*/
|
||||||
Map<String, Ref> getTags() {
|
public abstract Ref peel(Ref ref) throws IOException;
|
||||||
final Map<String, Ref> tags = new HashMap<String, Ref>();
|
|
||||||
for (final Ref r : readRefs().values()) {
|
|
||||||
if (r.getName().startsWith(R_TAGS))
|
|
||||||
tags.put(r.getName().substring(R_TAGS.length()), r);
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Ref> readRefs() {
|
|
||||||
final HashMap<String, Ref> avail = new HashMap<String, Ref>();
|
|
||||||
readPackedRefs(avail);
|
|
||||||
readLooseRefs(avail, REFS_SLASH, refsDir);
|
|
||||||
try {
|
|
||||||
final Ref r = readRefBasic(Constants.HEAD, 0);
|
|
||||||
if (r != null && r.getObjectId() != null)
|
|
||||||
avail.put(Constants.HEAD, r);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ignore here
|
|
||||||
}
|
|
||||||
db.fireRefsMaybeChanged();
|
|
||||||
return avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void readPackedRefs(final Map<String, Ref> avail) {
|
|
||||||
refreshPackedRefs();
|
|
||||||
avail.putAll(packedRefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readLooseRefs(final Map<String, Ref> avail,
|
|
||||||
final String prefix, final File dir) {
|
|
||||||
final File[] entries = dir.listFiles();
|
|
||||||
if (entries == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (final File ent : entries) {
|
|
||||||
final String entName = ent.getName();
|
|
||||||
if (".".equals(entName) || "..".equals(entName))
|
|
||||||
continue;
|
|
||||||
if (ent.isDirectory()) {
|
|
||||||
readLooseRefs(avail, prefix + entName + "/", ent);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
final Ref ref = readRefBasic(prefix + entName, 0);
|
|
||||||
if (ref != null)
|
|
||||||
avail.put(ref.getOrigName(), ref);
|
|
||||||
} catch (IOException e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref peel(final Ref ref) {
|
|
||||||
if (ref.isPeeled())
|
|
||||||
return ref;
|
|
||||||
ObjectId peeled = null;
|
|
||||||
try {
|
|
||||||
Object target = db.mapObject(ref.getObjectId(), ref.getName());
|
|
||||||
while (target instanceof Tag) {
|
|
||||||
final Tag tag = (Tag)target;
|
|
||||||
peeled = tag.getObjId();
|
|
||||||
if (Constants.TYPE_TAG.equals(tag.getType()))
|
|
||||||
target = db.mapObject(tag.getObjId(), ref.getName());
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore a read error. Callers will also get the same error
|
|
||||||
// if they try to use the result of getPeeledObjectId.
|
|
||||||
}
|
|
||||||
return new Ref(ref.getStorage(), ref.getName(), ref.getObjectId(), peeled, true);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private File fileForRef(final String name) {
|
|
||||||
if (name.startsWith(REFS_SLASH))
|
|
||||||
return new File(refsDir, name.substring(REFS_SLASH.length()));
|
|
||||||
return new File(gitDir, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Ref readRefBasic(final String name, final int depth) throws IOException {
|
|
||||||
return readRefBasic(name, name, depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized Ref readRefBasic(final String origName,
|
|
||||||
final String name, final int depth) throws IOException {
|
|
||||||
// Prefer loose ref to packed ref as the loose
|
|
||||||
// file can be more up-to-date than a packed one.
|
|
||||||
//
|
|
||||||
Ref ref = looseRefs.get(origName);
|
|
||||||
final File loose = fileForRef(name);
|
|
||||||
final long mtime = loose.lastModified();
|
|
||||||
if (ref != null) {
|
|
||||||
Long cachedlastModified = looseRefsMTime.get(name);
|
|
||||||
if (cachedlastModified != null && cachedlastModified == mtime) {
|
|
||||||
if (packedRefs.containsKey(origName))
|
|
||||||
return new Ref(Storage.LOOSE_PACKED, origName, ref
|
|
||||||
.getObjectId(), ref.getPeeledObjectId(), ref
|
|
||||||
.isPeeled());
|
|
||||||
else
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
looseRefs.remove(origName);
|
|
||||||
looseRefsMTime.remove(origName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtime == 0) {
|
|
||||||
// If last modified is 0 the file does not exist.
|
|
||||||
// Try packed cache.
|
|
||||||
//
|
|
||||||
ref = packedRefs.get(name);
|
|
||||||
if (ref != null)
|
|
||||||
if (!ref.getOrigName().equals(origName))
|
|
||||||
ref = new Ref(Storage.LOOSE_PACKED, origName, name, ref.getObjectId());
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
String line = null;
|
|
||||||
try {
|
|
||||||
Long cachedlastModified = looseRefsMTime.get(name);
|
|
||||||
if (cachedlastModified != null && cachedlastModified == mtime) {
|
|
||||||
line = looseSymRefs.get(name);
|
|
||||||
}
|
|
||||||
if (line == null) {
|
|
||||||
line = readLine(loose);
|
|
||||||
looseRefsMTime.put(name, mtime);
|
|
||||||
looseSymRefs.put(name, line);
|
|
||||||
}
|
|
||||||
} catch (FileNotFoundException notLoose) {
|
|
||||||
return packedRefs.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line == null || line.length() == 0) {
|
|
||||||
looseRefs.remove(origName);
|
|
||||||
looseRefsMTime.remove(origName);
|
|
||||||
return new Ref(Ref.Storage.LOOSE, origName, name, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.startsWith("ref: ")) {
|
|
||||||
if (depth >= 5) {
|
|
||||||
throw new IOException("Exceeded maximum ref depth of " + depth
|
|
||||||
+ " at " + name + ". Circular reference?");
|
|
||||||
}
|
|
||||||
|
|
||||||
final String target = line.substring("ref: ".length());
|
|
||||||
Ref r = readRefBasic(target, target, depth + 1);
|
|
||||||
Long cachedMtime = looseRefsMTime.get(name);
|
|
||||||
if (cachedMtime != null && cachedMtime != mtime)
|
|
||||||
setModified();
|
|
||||||
looseRefsMTime.put(name, mtime);
|
|
||||||
if (r == null)
|
|
||||||
return new Ref(Ref.Storage.LOOSE, origName, target, null);
|
|
||||||
if (!origName.equals(r.getName()))
|
|
||||||
r = new Ref(Ref.Storage.LOOSE_PACKED, origName, r.getName(), r.getObjectId(), r.getPeeledObjectId(), true);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified();
|
|
||||||
|
|
||||||
final ObjectId id;
|
|
||||||
try {
|
|
||||||
id = ObjectId.fromString(line);
|
|
||||||
} catch (IllegalArgumentException notRef) {
|
|
||||||
throw new IOException("Not a ref: " + name + ": " + line);
|
|
||||||
}
|
|
||||||
|
|
||||||
Storage storage;
|
|
||||||
if (packedRefs.containsKey(name))
|
|
||||||
storage = Ref.Storage.LOOSE_PACKED;
|
|
||||||
else
|
|
||||||
storage = Ref.Storage.LOOSE;
|
|
||||||
ref = new Ref(storage, name, id);
|
|
||||||
looseRefs.put(name, ref);
|
|
||||||
looseRefsMTime.put(name, mtime);
|
|
||||||
|
|
||||||
if (!origName.equals(name)) {
|
|
||||||
ref = new Ref(Ref.Storage.LOOSE, origName, name, id);
|
|
||||||
looseRefs.put(origName, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void refreshPackedRefs() {
|
|
||||||
final long currTime = packedRefsFile.lastModified();
|
|
||||||
final long currLen = currTime == 0 ? 0 : packedRefsFile.length();
|
|
||||||
if (currTime == packedRefsLastModified && currLen == packedRefsLength)
|
|
||||||
return;
|
|
||||||
if (currTime == 0) {
|
|
||||||
packedRefsLastModified = 0;
|
|
||||||
packedRefsLength = 0;
|
|
||||||
packedRefs = new HashMap<String, Ref>();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, Ref> newPackedRefs = new HashMap<String, Ref>();
|
|
||||||
try {
|
|
||||||
final BufferedReader b = openReader(packedRefsFile);
|
|
||||||
try {
|
|
||||||
String p;
|
|
||||||
Ref last = null;
|
|
||||||
while ((p = b.readLine()) != null) {
|
|
||||||
if (p.charAt(0) == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (p.charAt(0) == '^') {
|
|
||||||
if (last == null)
|
|
||||||
throw new IOException("Peeled line before ref.");
|
|
||||||
|
|
||||||
final ObjectId id = ObjectId.fromString(p.substring(1));
|
|
||||||
last = new Ref(Ref.Storage.PACKED, last.getName(), last
|
|
||||||
.getName(), last.getObjectId(), id, true);
|
|
||||||
newPackedRefs.put(last.getName(), last);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int sp = p.indexOf(' ');
|
|
||||||
final ObjectId id = ObjectId.fromString(p.substring(0, sp));
|
|
||||||
final String name = copy(p, sp + 1, p.length());
|
|
||||||
last = new Ref(Ref.Storage.PACKED, name, name, id);
|
|
||||||
newPackedRefs.put(last.getName(), last);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
b.close();
|
|
||||||
}
|
|
||||||
packedRefsLastModified = currTime;
|
|
||||||
packedRefsLength = currLen;
|
|
||||||
packedRefs = newPackedRefs;
|
|
||||||
setModified();
|
|
||||||
} catch (FileNotFoundException noPackedRefs) {
|
|
||||||
// Ignore it and leave the new map empty.
|
|
||||||
//
|
|
||||||
packedRefsLastModified = 0;
|
|
||||||
packedRefsLength = 0;
|
|
||||||
packedRefs = newPackedRefs;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Cannot read packed refs", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String copy(final String src, final int off, final int end) {
|
|
||||||
return new StringBuilder(end - off).append(src, off, end).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void lockAndWriteFile(File file, byte[] content) throws IOException {
|
|
||||||
String name = file.getName();
|
|
||||||
final LockFile lck = new LockFile(file);
|
|
||||||
if (!lck.lock())
|
|
||||||
throw new ObjectWritingException("Unable to lock " + name);
|
|
||||||
try {
|
|
||||||
lck.write(content);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
throw new ObjectWritingException("Unable to write " + name, ioe);
|
|
||||||
}
|
|
||||||
if (!lck.commit())
|
|
||||||
throw new ObjectWritingException("Unable to write " + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void removePackedRef(String name) throws IOException {
|
|
||||||
packedRefs.remove(name);
|
|
||||||
writePackedRefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writePackedRefs() throws IOException {
|
|
||||||
new RefWriter(packedRefs.values()) {
|
|
||||||
@Override
|
|
||||||
protected void writeFile(String name, byte[] content) throws IOException {
|
|
||||||
lockAndWriteFile(new File(db.getDirectory(), name), content);
|
|
||||||
}
|
|
||||||
}.writePackedRefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readLine(final File file)
|
|
||||||
throws FileNotFoundException, IOException {
|
|
||||||
final byte[] buf = IO.readFully(file, 4096);
|
|
||||||
int n = buf.length;
|
|
||||||
|
|
||||||
// remove trailing whitespaces
|
|
||||||
while (n > 0 && Character.isWhitespace(buf[n - 1]))
|
|
||||||
n--;
|
|
||||||
|
|
||||||
if (n == 0)
|
|
||||||
return null;
|
|
||||||
return RawParseUtils.decode(buf, 0, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BufferedReader openReader(final File fileLocation)
|
|
||||||
throws FileNotFoundException {
|
|
||||||
return new BufferedReader(new InputStreamReader(new FileInputStream(
|
|
||||||
fileLocation), Constants.CHARSET));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* Copyright (C) 2009, Robin Rosenberg
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate.Result;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename any reference stored by {@link RefDirectory}.
|
||||||
|
* <p>
|
||||||
|
* This class works by first renaming the source reference to a temporary name,
|
||||||
|
* then renaming the temporary name to the final destination reference.
|
||||||
|
* <p>
|
||||||
|
* This strategy permits switching a reference like {@code refs/heads/foo},
|
||||||
|
* which is a file, to {@code refs/heads/foo/bar}, which is stored inside a
|
||||||
|
* directory that happens to match the source name.
|
||||||
|
*/
|
||||||
|
class RefDirectoryRename extends RefRename {
|
||||||
|
private final RefDirectory refdb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of the source reference at the start of the rename.
|
||||||
|
* <p>
|
||||||
|
* At the end of the rename the destination reference must have this same
|
||||||
|
* value, otherwise we have a concurrent update and the rename must fail
|
||||||
|
* without making any changes.
|
||||||
|
*/
|
||||||
|
private ObjectId objId;
|
||||||
|
|
||||||
|
/** True if HEAD must be moved to the destination reference. */
|
||||||
|
private boolean updateHEAD;
|
||||||
|
|
||||||
|
/** A reference we backup {@link #objId} into during the rename. */
|
||||||
|
private RefDirectoryUpdate tmp;
|
||||||
|
|
||||||
|
RefDirectoryRename(RefDirectoryUpdate src, RefDirectoryUpdate dst) {
|
||||||
|
super(src, dst);
|
||||||
|
refdb = src.getRefDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Result doRename() throws IOException {
|
||||||
|
if (source.getRef().isSymbolic())
|
||||||
|
return Result.IO_FAILURE; // not supported
|
||||||
|
|
||||||
|
final RevWalk rw = new RevWalk(refdb.getRepository());
|
||||||
|
objId = source.getOldObjectId();
|
||||||
|
updateHEAD = needToUpdateHEAD();
|
||||||
|
tmp = refdb.newTemporaryUpdate();
|
||||||
|
try {
|
||||||
|
// First backup the source so its never unreachable.
|
||||||
|
tmp.setNewObjectId(objId);
|
||||||
|
tmp.setForceUpdate(true);
|
||||||
|
tmp.disableRefLog();
|
||||||
|
switch (tmp.update(rw)) {
|
||||||
|
case NEW:
|
||||||
|
case FORCED:
|
||||||
|
case NO_CHANGE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return tmp.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the source's log under the temporary name, we must do
|
||||||
|
// this before we delete the source, otherwise we lose the log.
|
||||||
|
if (!renameLog(source, tmp))
|
||||||
|
return Result.IO_FAILURE;
|
||||||
|
|
||||||
|
// If HEAD has to be updated, link it now to destination.
|
||||||
|
// We have to link before we delete, otherwise the delete
|
||||||
|
// fails because its the current branch.
|
||||||
|
RefUpdate dst = destination;
|
||||||
|
if (updateHEAD) {
|
||||||
|
if (!linkHEAD(destination)) {
|
||||||
|
renameLog(tmp, source);
|
||||||
|
return Result.LOCK_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the update operation so HEAD will log the rename.
|
||||||
|
dst = refdb.newUpdate(Constants.HEAD, false);
|
||||||
|
dst.setRefLogIdent(destination.getRefLogIdent());
|
||||||
|
dst.setRefLogMessage(destination.getRefLogMessage(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the source name so its path is free for replacement.
|
||||||
|
source.setExpectedOldObjectId(objId);
|
||||||
|
source.setForceUpdate(true);
|
||||||
|
source.disableRefLog();
|
||||||
|
if (source.delete(rw) != Result.FORCED) {
|
||||||
|
renameLog(tmp, source);
|
||||||
|
if (updateHEAD)
|
||||||
|
linkHEAD(source);
|
||||||
|
return source.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the log to the destination.
|
||||||
|
if (!renameLog(tmp, destination)) {
|
||||||
|
renameLog(tmp, source);
|
||||||
|
source.setExpectedOldObjectId(ObjectId.zeroId());
|
||||||
|
source.setNewObjectId(objId);
|
||||||
|
source.update(rw);
|
||||||
|
if (updateHEAD)
|
||||||
|
linkHEAD(source);
|
||||||
|
return Result.IO_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the destination, logging the rename during the creation.
|
||||||
|
dst.setExpectedOldObjectId(ObjectId.zeroId());
|
||||||
|
dst.setNewObjectId(objId);
|
||||||
|
if (dst.update(rw) != Result.NEW) {
|
||||||
|
// If we didn't create the destination we have to undo
|
||||||
|
// our work. Put the log back and restore source.
|
||||||
|
if (renameLog(destination, tmp))
|
||||||
|
renameLog(tmp, source);
|
||||||
|
source.setExpectedOldObjectId(ObjectId.zeroId());
|
||||||
|
source.setNewObjectId(objId);
|
||||||
|
source.update(rw);
|
||||||
|
if (updateHEAD)
|
||||||
|
linkHEAD(source);
|
||||||
|
return dst.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.RENAMED;
|
||||||
|
} finally {
|
||||||
|
// Always try to free the temporary name.
|
||||||
|
try {
|
||||||
|
refdb.delete(tmp);
|
||||||
|
} catch (IOException err) {
|
||||||
|
refdb.fileFor(tmp.getName()).delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean renameLog(RefUpdate src, RefUpdate dst) {
|
||||||
|
File srcLog = refdb.logFor(src.getName());
|
||||||
|
File dstLog = refdb.logFor(dst.getName());
|
||||||
|
|
||||||
|
if (!srcLog.exists())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!rename(srcLog, dstLog))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int levels = RefDirectory.levelsIn(src.getName()) - 2;
|
||||||
|
RefDirectory.delete(srcLog, levels);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
rename(dstLog, srcLog);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean rename(File src, File dst) {
|
||||||
|
if (src.renameTo(dst))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
File dir = dst.getParentFile();
|
||||||
|
if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory())
|
||||||
|
return false;
|
||||||
|
return src.renameTo(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean linkHEAD(RefUpdate target) {
|
||||||
|
try {
|
||||||
|
RefUpdate u = refdb.newUpdate(Constants.HEAD, false);
|
||||||
|
u.disableRefLog();
|
||||||
|
switch (u.link(target.getName())) {
|
||||||
|
case NEW:
|
||||||
|
case FORCED:
|
||||||
|
case NO_CHANGE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2010, Google Inc.
|
||||||
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.lib.Constants.encode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/** Updates any reference stored by {@link RefDirectory}. */
|
||||||
|
class RefDirectoryUpdate extends RefUpdate {
|
||||||
|
private final RefDirectory database;
|
||||||
|
|
||||||
|
private LockFile lock;
|
||||||
|
|
||||||
|
RefDirectoryUpdate(final RefDirectory r, final Ref ref) {
|
||||||
|
super(ref);
|
||||||
|
database = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RefDirectory getRefDatabase() {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Repository getRepository() {
|
||||||
|
return database.getRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean tryLock(boolean deref) throws IOException {
|
||||||
|
Ref dst = getRef();
|
||||||
|
if (deref)
|
||||||
|
dst = dst.getLeaf();
|
||||||
|
String name = dst.getName();
|
||||||
|
lock = new LockFile(database.fileFor(name));
|
||||||
|
if (lock.lock()) {
|
||||||
|
dst = database.getRef(name);
|
||||||
|
setOldObjectId(dst != null ? dst.getObjectId() : null);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void unlock() {
|
||||||
|
if (lock != null) {
|
||||||
|
lock.unlock();
|
||||||
|
lock = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Result doUpdate(final Result status) throws IOException {
|
||||||
|
lock.setNeedStatInformation(true);
|
||||||
|
lock.write(getNewObjectId());
|
||||||
|
|
||||||
|
String msg = getRefLogMessage();
|
||||||
|
if (msg != null) {
|
||||||
|
if (isRefLogIncludingResult()) {
|
||||||
|
String strResult = toResultString(status);
|
||||||
|
if (strResult != null) {
|
||||||
|
if (msg.length() > 0)
|
||||||
|
msg = msg + ": " + strResult;
|
||||||
|
else
|
||||||
|
msg = strResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
database.log(this, msg, true);
|
||||||
|
}
|
||||||
|
if (!lock.commit())
|
||||||
|
return Result.LOCK_FAILURE;
|
||||||
|
database.stored(this, lock.getCommitLastModified());
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toResultString(final Result status) {
|
||||||
|
switch (status) {
|
||||||
|
case FORCED:
|
||||||
|
return "forced-update";
|
||||||
|
case FAST_FORWARD:
|
||||||
|
return "fast forward";
|
||||||
|
case NEW:
|
||||||
|
return "created";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Result doDelete(final Result status) throws IOException {
|
||||||
|
if (getRef().getLeaf().getStorage() != Ref.Storage.NEW)
|
||||||
|
database.delete(this);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Result doLink(final String target) throws IOException {
|
||||||
|
lock.setNeedStatInformation(true);
|
||||||
|
lock.write(encode(RefDirectory.SYMREF + target + '\n'));
|
||||||
|
|
||||||
|
String msg = getRefLogMessage();
|
||||||
|
if (msg != null)
|
||||||
|
database.log(this, msg, false);
|
||||||
|
if (!lock.commit())
|
||||||
|
return Result.LOCK_FAILURE;
|
||||||
|
database.storedSymbolicRef(this, lock.getCommitLastModified(), target);
|
||||||
|
|
||||||
|
if (getRef().getStorage() == Ref.Storage.NEW)
|
||||||
|
return Result.NEW;
|
||||||
|
return Result.FORCED;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
|
|
||||||
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
|
|
||||||
* Copyright (C) 2009, Google Inc.
|
|
||||||
* Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
|
||||||
* Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org>
|
|
||||||
* and other copyright owners as documented in the project's IP log.
|
|
||||||
*
|
|
||||||
* This program and the accompanying materials are made available
|
|
||||||
* under the terms of the Eclipse Distribution License v1.0 which
|
|
||||||
* accompanies this distribution, is reproduced below, and is
|
|
||||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or
|
|
||||||
* without modification, are permitted provided that the following
|
|
||||||
* conditions are met:
|
|
||||||
*
|
|
||||||
* - Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* - Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following
|
|
||||||
* disclaimer in the documentation and/or other materials provided
|
|
||||||
* with the distribution.
|
|
||||||
*
|
|
||||||
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
|
||||||
* names of its contributors may be used to endorse or promote
|
|
||||||
* products derived from this software without specific prior
|
|
||||||
* written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
||||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
||||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.eclipse.jgit.lib;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class to work with reflog files
|
|
||||||
*
|
|
||||||
* @author Dave Watson
|
|
||||||
*/
|
|
||||||
public class RefLogWriter {
|
|
||||||
static void append(final RefUpdate u, final String msg) throws IOException {
|
|
||||||
final ObjectId oldId = u.getOldObjectId();
|
|
||||||
final ObjectId newId = u.getNewObjectId();
|
|
||||||
final Repository db = u.getRepository();
|
|
||||||
final PersonIdent ident = u.getRefLogIdent();
|
|
||||||
|
|
||||||
appendOneRecord(oldId, newId, ident, msg, db, u.getName());
|
|
||||||
if (!u.getName().equals(u.getOrigName()))
|
|
||||||
appendOneRecord(oldId, newId, ident, msg, db, u.getOrigName());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void append(RefRename refRename, String logName, String msg) throws IOException {
|
|
||||||
final ObjectId id = refRename.getObjectId();
|
|
||||||
final Repository db = refRename.getRepository();
|
|
||||||
final PersonIdent ident = refRename.getRefLogIdent();
|
|
||||||
appendOneRecord(id, id, ident, msg, db, logName);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void renameTo(final Repository db, final RefUpdate from,
|
|
||||||
final RefUpdate to) throws IOException {
|
|
||||||
final File logdir = new File(db.getDirectory(), Constants.LOGS);
|
|
||||||
final File reflogFrom = new File(logdir, from.getName());
|
|
||||||
if (!reflogFrom.exists())
|
|
||||||
return;
|
|
||||||
final File reflogTo = new File(logdir, to.getName());
|
|
||||||
final File reflogToDir = reflogTo.getParentFile();
|
|
||||||
File tmp = new File(logdir, "tmp-renamed-log.." + Thread.currentThread().getId());
|
|
||||||
if (!reflogFrom.renameTo(tmp)) {
|
|
||||||
throw new IOException("Cannot rename " + reflogFrom + " to (" + tmp
|
|
||||||
+ ")" + reflogTo);
|
|
||||||
}
|
|
||||||
RefUpdate.deleteEmptyDir(reflogFrom, RefUpdate.count(from.getName(),
|
|
||||||
'/'));
|
|
||||||
if (!reflogToDir.exists() && !reflogToDir.mkdirs()) {
|
|
||||||
throw new IOException("Cannot create directory " + reflogToDir);
|
|
||||||
}
|
|
||||||
if (!tmp.renameTo(reflogTo)) {
|
|
||||||
throw new IOException("Cannot rename (" + tmp + ")" + reflogFrom
|
|
||||||
+ " to " + reflogTo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void appendOneRecord(final ObjectId oldId,
|
|
||||||
final ObjectId newId, PersonIdent ident, final String msg,
|
|
||||||
final Repository db, final String refName) throws IOException {
|
|
||||||
if (ident == null)
|
|
||||||
ident = new PersonIdent(db);
|
|
||||||
else
|
|
||||||
ident = new PersonIdent(ident);
|
|
||||||
|
|
||||||
final StringBuilder r = new StringBuilder();
|
|
||||||
r.append(ObjectId.toString(oldId));
|
|
||||||
r.append(' ');
|
|
||||||
r.append(ObjectId.toString(newId));
|
|
||||||
r.append(' ');
|
|
||||||
r.append(ident.toExternalString());
|
|
||||||
r.append('\t');
|
|
||||||
r.append(msg);
|
|
||||||
r.append('\n');
|
|
||||||
|
|
||||||
final byte[] rec = Constants.encode(r.toString());
|
|
||||||
final File logdir = new File(db.getDirectory(), Constants.LOGS);
|
|
||||||
final File reflog = new File(logdir, refName);
|
|
||||||
if (reflog.exists() || db.getConfig().getCore().isLogAllRefUpdates()) {
|
|
||||||
final File refdir = reflog.getParentFile();
|
|
||||||
|
|
||||||
if (!refdir.exists() && !refdir.mkdirs())
|
|
||||||
throw new IOException("Cannot create directory " + refdir);
|
|
||||||
|
|
||||||
final FileOutputStream out = new FileOutputStream(reflog, true);
|
|
||||||
try {
|
|
||||||
out.write(rec);
|
|
||||||
} finally {
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes reflog entry for ref specified by refName
|
|
||||||
*
|
|
||||||
* @param repo
|
|
||||||
* repository to use
|
|
||||||
* @param oldCommit
|
|
||||||
* previous commit
|
|
||||||
* @param commit
|
|
||||||
* new commit
|
|
||||||
* @param message
|
|
||||||
* reflog message
|
|
||||||
* @param refName
|
|
||||||
* full ref name
|
|
||||||
* @throws IOException
|
|
||||||
* @deprecated rely upon {@link RefUpdate}'s automatic logging instead.
|
|
||||||
*/
|
|
||||||
public static void writeReflog(Repository repo, ObjectId oldCommit,
|
|
||||||
ObjectId commit, String message, String refName) throws IOException {
|
|
||||||
appendOneRecord(oldCommit, commit, null, message, repo, refName);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,8 @@
|
||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2009-2010, Google Inc.
|
||||||
* Copyright (C) 2009, Robin Rosenberg
|
* Copyright (C) 2009, Robin Rosenberg
|
||||||
* Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -49,25 +51,96 @@
|
||||||
import org.eclipse.jgit.lib.RefUpdate.Result;
|
import org.eclipse.jgit.lib.RefUpdate.Result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A RefUpdate combination for renaming a ref
|
* A RefUpdate combination for renaming a reference.
|
||||||
|
* <p>
|
||||||
|
* If the source reference is currently pointed to by {@code HEAD}, then the
|
||||||
|
* HEAD symbolic reference is updated to point to the new destination.
|
||||||
*/
|
*/
|
||||||
public class RefRename {
|
public abstract class RefRename {
|
||||||
private RefUpdate newToUpdate;
|
/** Update operation to read and delete the source reference. */
|
||||||
|
protected final RefUpdate source;
|
||||||
|
|
||||||
private RefUpdate oldFromDelete;
|
/** Update operation to create/overwrite the destination reference. */
|
||||||
|
protected final RefUpdate destination;
|
||||||
|
|
||||||
private Result renameResult = Result.NOT_ATTEMPTED;
|
private Result result = Result.NOT_ATTEMPTED;
|
||||||
|
|
||||||
RefRename(final RefUpdate toUpdate, final RefUpdate fromUpdate) {
|
/**
|
||||||
newToUpdate = toUpdate;
|
* Initialize a new rename operation.
|
||||||
oldFromDelete = fromUpdate;
|
*
|
||||||
|
* @param src
|
||||||
|
* operation to read and delete the source.
|
||||||
|
* @param dst
|
||||||
|
* operation to create (or overwrite) the destination.
|
||||||
|
*/
|
||||||
|
protected RefRename(final RefUpdate src, final RefUpdate dst) {
|
||||||
|
source = src;
|
||||||
|
destination = dst;
|
||||||
|
|
||||||
|
Repository repo = destination.getRepository();
|
||||||
|
String cmd = "";
|
||||||
|
if (source.getName().startsWith(Constants.R_HEADS)
|
||||||
|
&& destination.getName().startsWith(Constants.R_HEADS))
|
||||||
|
cmd = "Branch: ";
|
||||||
|
setRefLogMessage(cmd + "renamed "
|
||||||
|
+ repo.shortenRefName(source.getName()) + " to "
|
||||||
|
+ repo.shortenRefName(destination.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return identity of the user making the change in the reflog. */
|
||||||
|
public PersonIdent getRefLogIdent() {
|
||||||
|
return destination.getRefLogIdent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the identity of the user appearing in the reflog.
|
||||||
|
* <p>
|
||||||
|
* The timestamp portion of the identity is ignored. A new identity with the
|
||||||
|
* current timestamp will be created automatically when the rename occurs
|
||||||
|
* and the log record is written.
|
||||||
|
*
|
||||||
|
* @param pi
|
||||||
|
* identity of the user. If null the identity will be
|
||||||
|
* automatically determined based on the repository
|
||||||
|
* configuration.
|
||||||
|
*/
|
||||||
|
public void setRefLogIdent(final PersonIdent pi) {
|
||||||
|
destination.setRefLogIdent(pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message to include in the reflog.
|
||||||
|
*
|
||||||
|
* @return message the caller wants to include in the reflog; null if the
|
||||||
|
* rename should not be logged.
|
||||||
|
*/
|
||||||
|
public String getRefLogMessage() {
|
||||||
|
return destination.getRefLogMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the message to include in the reflog.
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* the message to describe this change.
|
||||||
|
*/
|
||||||
|
public void setRefLogMessage(final String msg) {
|
||||||
|
if (msg == null)
|
||||||
|
disableRefLog();
|
||||||
|
else
|
||||||
|
destination.setRefLogMessage(msg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Don't record this rename in the ref's associated reflog. */
|
||||||
|
public void disableRefLog() {
|
||||||
|
destination.setRefLogMessage("", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return result of rename operation
|
* @return result of rename operation
|
||||||
*/
|
*/
|
||||||
public Result getResult() {
|
public Result getResult() {
|
||||||
return renameResult;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,101 +148,33 @@ public Result getResult() {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Result rename() throws IOException {
|
public Result rename() throws IOException {
|
||||||
Ref oldRef = oldFromDelete.db.readRef(Constants.HEAD);
|
|
||||||
boolean renameHEADtoo = oldRef != null
|
|
||||||
&& oldRef.getName().equals(oldFromDelete.getName());
|
|
||||||
Repository db = oldFromDelete.getRepository();
|
|
||||||
try {
|
try {
|
||||||
RefLogWriter.renameTo(db, oldFromDelete,
|
result = doRename();
|
||||||
newToUpdate);
|
return result;
|
||||||
newToUpdate.setRefLogMessage(null, false);
|
} catch (IOException err) {
|
||||||
String tmpRefName = "RENAMED-REF.." + Thread.currentThread().getId();
|
result = Result.IO_FAILURE;
|
||||||
RefUpdate tmpUpdateRef = db.updateRef(tmpRefName);
|
throw err;
|
||||||
if (renameHEADtoo) {
|
|
||||||
try {
|
|
||||||
oldFromDelete.db.link(Constants.HEAD, tmpRefName);
|
|
||||||
} catch (IOException e) {
|
|
||||||
RefLogWriter.renameTo(db,
|
|
||||||
newToUpdate, oldFromDelete);
|
|
||||||
return renameResult = Result.LOCK_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpUpdateRef.setNewObjectId(oldFromDelete.getOldObjectId());
|
|
||||||
tmpUpdateRef.setForceUpdate(true);
|
|
||||||
Result update = tmpUpdateRef.update();
|
|
||||||
if (update != Result.FORCED && update != Result.NEW && update != Result.NO_CHANGE) {
|
|
||||||
RefLogWriter.renameTo(db,
|
|
||||||
newToUpdate, oldFromDelete);
|
|
||||||
if (renameHEADtoo) {
|
|
||||||
oldFromDelete.db.link(Constants.HEAD, oldFromDelete.getName());
|
|
||||||
}
|
|
||||||
return renameResult = update;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldFromDelete.setExpectedOldObjectId(oldFromDelete.getOldObjectId());
|
|
||||||
oldFromDelete.setForceUpdate(true);
|
|
||||||
Result delete = oldFromDelete.delete();
|
|
||||||
if (delete != Result.FORCED) {
|
|
||||||
if (db.getRef(
|
|
||||||
oldFromDelete.getName()) != null) {
|
|
||||||
RefLogWriter.renameTo(db,
|
|
||||||
newToUpdate, oldFromDelete);
|
|
||||||
if (renameHEADtoo) {
|
|
||||||
oldFromDelete.db.link(Constants.HEAD, oldFromDelete
|
|
||||||
.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return renameResult = delete;
|
|
||||||
}
|
|
||||||
|
|
||||||
newToUpdate.setNewObjectId(tmpUpdateRef.getNewObjectId());
|
|
||||||
Result updateResult = newToUpdate.update();
|
|
||||||
if (updateResult != Result.NEW) {
|
|
||||||
RefLogWriter.renameTo(db, newToUpdate, oldFromDelete);
|
|
||||||
if (renameHEADtoo) {
|
|
||||||
oldFromDelete.db.link(Constants.HEAD, oldFromDelete.getName());
|
|
||||||
}
|
|
||||||
oldFromDelete.setExpectedOldObjectId(null);
|
|
||||||
oldFromDelete.setNewObjectId(oldFromDelete.getOldObjectId());
|
|
||||||
oldFromDelete.setForceUpdate(true);
|
|
||||||
oldFromDelete.setRefLogMessage(null, false);
|
|
||||||
Result undelete = oldFromDelete.update();
|
|
||||||
if (undelete != Result.NEW && undelete != Result.LOCK_FAILURE)
|
|
||||||
return renameResult = Result.IO_FAILURE;
|
|
||||||
return renameResult = Result.LOCK_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renameHEADtoo) {
|
|
||||||
oldFromDelete.db.link(Constants.HEAD, newToUpdate.getName());
|
|
||||||
} else {
|
|
||||||
db.fireRefsMaybeChanged();
|
|
||||||
}
|
|
||||||
RefLogWriter.append(this, newToUpdate.getName(), "Branch: renamed "
|
|
||||||
+ db.shortenRefName(oldFromDelete.getName()) + " to "
|
|
||||||
+ db.shortenRefName(newToUpdate.getName()));
|
|
||||||
if (renameHEADtoo)
|
|
||||||
RefLogWriter.append(this, Constants.HEAD, "Branch: renamed "
|
|
||||||
+ db.shortenRefName(oldFromDelete.getName()) + " to "
|
|
||||||
+ db.shortenRefName(newToUpdate.getName()));
|
|
||||||
return renameResult = Result.RENAMED;
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectId getObjectId() {
|
/**
|
||||||
return oldFromDelete.getOldObjectId();
|
* @return the result of the rename operation.
|
||||||
}
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected abstract Result doRename() throws IOException;
|
||||||
|
|
||||||
Repository getRepository() {
|
/**
|
||||||
return oldFromDelete.getRepository();
|
* @return true if the {@code Constants#HEAD} reference needs to be linked
|
||||||
}
|
* to the new destination name.
|
||||||
|
* @throws IOException
|
||||||
PersonIdent getRefLogIdent() {
|
* the current value of {@code HEAD} cannot be read.
|
||||||
return newToUpdate.getRefLogIdent();
|
*/
|
||||||
}
|
protected boolean needToUpdateHEAD() throws IOException {
|
||||||
|
Ref head = source.getRefDatabase().getRef(Constants.HEAD);
|
||||||
String getToName() {
|
if (head.isSymbolic()) {
|
||||||
return newToUpdate.getName();
|
head = head.getTarget();
|
||||||
|
return head.getName().equals(source.getName());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* Copyright (C) 2008-2009, Google Inc.
|
|
||||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
|
@ -45,19 +44,17 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.lib;
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
import org.eclipse.jgit.lib.Ref.Storage;
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates any locally stored ref.
|
* Creates, updates or deletes any reference.
|
||||||
*/
|
*/
|
||||||
public class RefUpdate {
|
public abstract class RefUpdate {
|
||||||
/** Status of an update request. */
|
/** Status of an update request. */
|
||||||
public static enum Result {
|
public static enum Result {
|
||||||
/** The ref update/delete has not been attempted by the caller. */
|
/** The ref update/delete has not been attempted by the caller. */
|
||||||
|
@ -142,12 +139,6 @@ public static enum Result {
|
||||||
RENAMED
|
RENAMED
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Repository the ref is stored in. */
|
|
||||||
final RefDatabase db;
|
|
||||||
|
|
||||||
/** Location of the loose file holding the value of this ref. */
|
|
||||||
final File looseFile;
|
|
||||||
|
|
||||||
/** New value the caller wants this ref to have. */
|
/** New value the caller wants this ref to have. */
|
||||||
private ObjectId newValue;
|
private ObjectId newValue;
|
||||||
|
|
||||||
|
@ -170,22 +161,63 @@ public static enum Result {
|
||||||
private ObjectId expValue;
|
private ObjectId expValue;
|
||||||
|
|
||||||
/** Result of the update operation. */
|
/** Result of the update operation. */
|
||||||
Result result = Result.NOT_ATTEMPTED;
|
private Result result = Result.NOT_ATTEMPTED;
|
||||||
|
|
||||||
private final Ref ref;
|
private final Ref ref;
|
||||||
|
|
||||||
RefUpdate(final RefDatabase r, final Ref ref, final File f) {
|
RefUpdate(final Ref ref) {
|
||||||
db = r;
|
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
oldValue = ref.getObjectId();
|
oldValue = ref.getObjectId();
|
||||||
looseFile = f;
|
|
||||||
refLogMessage = "";
|
refLogMessage = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the repository the updated ref resides in */
|
/** @return the reference database this update modifies. */
|
||||||
public Repository getRepository() {
|
protected abstract RefDatabase getRefDatabase();
|
||||||
return db.getRepository();
|
|
||||||
}
|
/** @return the repository storing the database's objects. */
|
||||||
|
protected abstract Repository getRepository();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to acquire the lock on the reference.
|
||||||
|
* <p>
|
||||||
|
* If the locking was successful the implementor must set the current
|
||||||
|
* identity value by calling {@link #setOldObjectId(ObjectId)}.
|
||||||
|
*
|
||||||
|
* @param deref
|
||||||
|
* true if the lock should be taken against the leaf level
|
||||||
|
* reference; false if it should be taken exactly against the
|
||||||
|
* current reference.
|
||||||
|
* @return true if the lock was acquired and the reference is likely
|
||||||
|
* protected from concurrent modification; false if it failed.
|
||||||
|
* @throws IOException
|
||||||
|
* the lock couldn't be taken due to an unexpected storage
|
||||||
|
* failure, and not because of a concurrent update.
|
||||||
|
*/
|
||||||
|
protected abstract boolean tryLock(boolean deref) throws IOException;
|
||||||
|
|
||||||
|
/** Releases the lock taken by {@link #tryLock} if it succeeded. */
|
||||||
|
protected abstract void unlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param desiredResult
|
||||||
|
* @return {@code result}
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected abstract Result doUpdate(Result desiredResult) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param desiredResult
|
||||||
|
* @return {@code result}
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected abstract Result doDelete(Result desiredResult) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param target
|
||||||
|
* @return {@link Result#NEW} on success.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected abstract Result doLink(String target) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the ref this update will operate on.
|
* Get the name of the ref this update will operate on.
|
||||||
|
@ -193,16 +225,12 @@ public Repository getRepository() {
|
||||||
* @return name of underlying ref.
|
* @return name of underlying ref.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return ref.getName();
|
return getRef().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @return the reference this update will create or modify. */
|
||||||
* Get the requested name of the ref thit update will operate on
|
public Ref getRef() {
|
||||||
*
|
return ref;
|
||||||
* @return original (requested) name of the underlying ref.
|
|
||||||
*/
|
|
||||||
public String getOrigName() {
|
|
||||||
return ref.getOrigName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -295,12 +323,17 @@ public String getRefLogMessage() {
|
||||||
return refLogMessage;
|
return refLogMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return {@code true} if the ref log message should show the result. */
|
||||||
|
protected boolean isRefLogIncludingResult() {
|
||||||
|
return refLogIncludeResult;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the message to include in the reflog.
|
* Set the message to include in the reflog.
|
||||||
*
|
*
|
||||||
* @param msg
|
* @param msg
|
||||||
* the message to describe this change. It may be null
|
* the message to describe this change. It may be null if
|
||||||
* if appendStatus is null in order not to append to the reflog
|
* appendStatus is null in order not to append to the reflog
|
||||||
* @param appendStatus
|
* @param appendStatus
|
||||||
* true if the status of the ref change (fast-forward or
|
* true if the status of the ref change (fast-forward or
|
||||||
* forced-update) should be appended to the user supplied
|
* forced-update) should be appended to the user supplied
|
||||||
|
@ -339,6 +372,16 @@ public ObjectId getOldObjectId() {
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the old value of the ref.
|
||||||
|
*
|
||||||
|
* @param old
|
||||||
|
* the old value.
|
||||||
|
*/
|
||||||
|
protected void setOldObjectId(ObjectId old) {
|
||||||
|
oldValue = old;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the status of this update.
|
* Get the status of this update.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -378,7 +421,7 @@ public Result forceUpdate() throws IOException {
|
||||||
* This is the same as:
|
* This is the same as:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* return update(new RevWalk(repository));
|
* return update(new RevWalk(getRepository()));
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @return the result status of the update.
|
* @return the result status of the update.
|
||||||
|
@ -386,7 +429,7 @@ public Result forceUpdate() throws IOException {
|
||||||
* an unexpected IO error occurred while writing changes.
|
* an unexpected IO error occurred while writing changes.
|
||||||
*/
|
*/
|
||||||
public Result update() throws IOException {
|
public Result update() throws IOException {
|
||||||
return update(new RevWalk(db.getRepository()));
|
return update(new RevWalk(getRepository()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -404,7 +447,14 @@ public Result update() throws IOException {
|
||||||
public Result update(final RevWalk walk) throws IOException {
|
public Result update(final RevWalk walk) throws IOException {
|
||||||
requireCanDoUpdate();
|
requireCanDoUpdate();
|
||||||
try {
|
try {
|
||||||
return result = updateImpl(walk, new UpdateStore());
|
return result = updateImpl(walk, new Store() {
|
||||||
|
@Override
|
||||||
|
Result execute(Result status) throws IOException {
|
||||||
|
if (status == Result.NO_CHANGE)
|
||||||
|
return status;
|
||||||
|
return doUpdate(status);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (IOException x) {
|
} catch (IOException x) {
|
||||||
result = Result.IO_FAILURE;
|
result = Result.IO_FAILURE;
|
||||||
throw x;
|
throw x;
|
||||||
|
@ -417,14 +467,14 @@ public Result update(final RevWalk walk) throws IOException {
|
||||||
* This is the same as:
|
* This is the same as:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* return delete(new RevWalk(repository));
|
* return delete(new RevWalk(getRepository()));
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @return the result status of the delete.
|
* @return the result status of the delete.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Result delete() throws IOException {
|
public Result delete() throws IOException {
|
||||||
return delete(new RevWalk(db.getRepository()));
|
return delete(new RevWalk(getRepository()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -437,33 +487,83 @@ public Result delete() throws IOException {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Result delete(final RevWalk walk) throws IOException {
|
public Result delete(final RevWalk walk) throws IOException {
|
||||||
if (getName().startsWith(Constants.R_HEADS)) {
|
final String myName = getRef().getLeaf().getName();
|
||||||
final Ref head = db.readRef(Constants.HEAD);
|
if (myName.startsWith(Constants.R_HEADS)) {
|
||||||
if (head != null && getName().equals(head.getName()))
|
Ref head = getRefDatabase().getRef(Constants.HEAD);
|
||||||
return result = Result.REJECTED_CURRENT_BRANCH;
|
while (head.isSymbolic()) {
|
||||||
|
head = head.getTarget();
|
||||||
|
if (myName.equals(head.getName()))
|
||||||
|
return result = Result.REJECTED_CURRENT_BRANCH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return result = updateImpl(walk, new DeleteStore());
|
return result = updateImpl(walk, new Store() {
|
||||||
|
@Override
|
||||||
|
Result execute(Result status) throws IOException {
|
||||||
|
return doDelete(status);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (IOException x) {
|
} catch (IOException x) {
|
||||||
result = Result.IO_FAILURE;
|
result = Result.IO_FAILURE;
|
||||||
throw x;
|
throw x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace this reference with a symbolic reference to another reference.
|
||||||
|
* <p>
|
||||||
|
* This exact reference (not its traversed leaf) is replaced with a symbolic
|
||||||
|
* reference to the requested name.
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* name of the new target for this reference. The new target name
|
||||||
|
* must be absolute, so it must begin with {@code refs/}.
|
||||||
|
* @return {@link Result#NEW} or {@link Result#FORCED} on success.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public Result link(String target) throws IOException {
|
||||||
|
if (!target.startsWith(Constants.R_REFS))
|
||||||
|
throw new IllegalArgumentException("Not " + Constants.R_REFS);
|
||||||
|
if (getRefDatabase().isNameConflicting(getName()))
|
||||||
|
return Result.LOCK_FAILURE;
|
||||||
|
try {
|
||||||
|
if (!tryLock(false))
|
||||||
|
return Result.LOCK_FAILURE;
|
||||||
|
|
||||||
|
final Ref old = getRefDatabase().getRef(getName());
|
||||||
|
if (old != null && old.isSymbolic()) {
|
||||||
|
final Ref dst = old.getTarget();
|
||||||
|
if (target.equals(dst.getName()))
|
||||||
|
return result = Result.NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old != null && old.getObjectId() != null)
|
||||||
|
setOldObjectId(old.getObjectId());
|
||||||
|
|
||||||
|
final Ref dst = getRefDatabase().getRef(target);
|
||||||
|
if (dst != null && dst.getObjectId() != null)
|
||||||
|
setNewObjectId(dst.getObjectId());
|
||||||
|
|
||||||
|
return result = doLink(target);
|
||||||
|
} catch (IOException x) {
|
||||||
|
result = Result.IO_FAILURE;
|
||||||
|
throw x;
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Result updateImpl(final RevWalk walk, final Store store)
|
private Result updateImpl(final RevWalk walk, final Store store)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final LockFile lock;
|
|
||||||
RevObject newObj;
|
RevObject newObj;
|
||||||
RevObject oldObj;
|
RevObject oldObj;
|
||||||
|
|
||||||
if (isNameConflicting())
|
if (getRefDatabase().isNameConflicting(getName()))
|
||||||
return Result.LOCK_FAILURE;
|
|
||||||
lock = new LockFile(looseFile);
|
|
||||||
if (!lock.lock())
|
|
||||||
return Result.LOCK_FAILURE;
|
return Result.LOCK_FAILURE;
|
||||||
try {
|
try {
|
||||||
oldValue = db.idOf(getName());
|
if (!tryLock(true))
|
||||||
|
return Result.LOCK_FAILURE;
|
||||||
if (expValue != null) {
|
if (expValue != null) {
|
||||||
final ObjectId o;
|
final ObjectId o;
|
||||||
o = oldValue != null ? oldValue : ObjectId.zeroId();
|
o = oldValue != null ? oldValue : ObjectId.zeroId();
|
||||||
|
@ -471,41 +571,26 @@ private Result updateImpl(final RevWalk walk, final Store store)
|
||||||
return Result.LOCK_FAILURE;
|
return Result.LOCK_FAILURE;
|
||||||
}
|
}
|
||||||
if (oldValue == null)
|
if (oldValue == null)
|
||||||
return store.store(lock, Result.NEW);
|
return store.execute(Result.NEW);
|
||||||
|
|
||||||
newObj = safeParse(walk, newValue);
|
newObj = safeParse(walk, newValue);
|
||||||
oldObj = safeParse(walk, oldValue);
|
oldObj = safeParse(walk, oldValue);
|
||||||
if (newObj == oldObj)
|
if (newObj == oldObj)
|
||||||
return store.store(lock, Result.NO_CHANGE);
|
return store.execute(Result.NO_CHANGE);
|
||||||
|
|
||||||
if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
|
if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
|
||||||
if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj))
|
if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj))
|
||||||
return store.store(lock, Result.FAST_FORWARD);
|
return store.execute(Result.FAST_FORWARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isForceUpdate())
|
if (isForceUpdate())
|
||||||
return store.store(lock, Result.FORCED);
|
return store.execute(Result.FORCED);
|
||||||
return Result.REJECTED;
|
return Result.REJECTED;
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNameConflicting() throws IOException {
|
|
||||||
final String myName = getName();
|
|
||||||
final int lastSlash = myName.lastIndexOf('/');
|
|
||||||
if (lastSlash > 0)
|
|
||||||
if (db.getRepository().getRef(myName.substring(0, lastSlash)) != null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
final String rName = myName + "/";
|
|
||||||
for (Ref r : db.getAllRefs().values()) {
|
|
||||||
if (r.getName().startsWith(rName))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RevObject safeParse(final RevWalk rw, final AnyObjectId id)
|
private static RevObject safeParse(final RevWalk rw, final AnyObjectId id)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try {
|
try {
|
||||||
|
@ -520,123 +605,11 @@ private static RevObject safeParse(final RevWalk rw, final AnyObjectId id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result updateStore(final LockFile lock, final Result status)
|
|
||||||
throws IOException {
|
|
||||||
if (status == Result.NO_CHANGE)
|
|
||||||
return status;
|
|
||||||
lock.setNeedStatInformation(true);
|
|
||||||
lock.write(newValue);
|
|
||||||
String msg = getRefLogMessage();
|
|
||||||
if (msg != null) {
|
|
||||||
if (refLogIncludeResult) {
|
|
||||||
String strResult = toResultString(status);
|
|
||||||
if (strResult != null) {
|
|
||||||
if (msg.length() > 0)
|
|
||||||
msg = msg + ": " + strResult;
|
|
||||||
else
|
|
||||||
msg = strResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RefLogWriter.append(this, msg);
|
|
||||||
}
|
|
||||||
if (!lock.commit())
|
|
||||||
return Result.LOCK_FAILURE;
|
|
||||||
db.stored(this.ref.getOrigName(), ref.getName(), newValue, lock.getCommitLastModified());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String toResultString(final Result status) {
|
|
||||||
switch (status) {
|
|
||||||
case FORCED:
|
|
||||||
return "forced-update";
|
|
||||||
case FAST_FORWARD:
|
|
||||||
return "fast forward";
|
|
||||||
case NEW:
|
|
||||||
return "created";
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the abstraction of storing a ref update. This is because both
|
* Handle the abstraction of storing a ref update. This is because both
|
||||||
* updating and deleting of a ref have merge testing in common.
|
* updating and deleting of a ref have merge testing in common.
|
||||||
*/
|
*/
|
||||||
private abstract class Store {
|
private abstract class Store {
|
||||||
abstract Result store(final LockFile lock, final Result status)
|
abstract Result execute(Result status) throws IOException;
|
||||||
throws IOException;
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateStore extends Store {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result store(final LockFile lock, final Result status)
|
|
||||||
throws IOException {
|
|
||||||
return updateStore(lock, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DeleteStore extends Store {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Result store(LockFile lock, Result status) throws IOException {
|
|
||||||
Storage storage = ref.getStorage();
|
|
||||||
if (storage == Storage.NEW)
|
|
||||||
return status;
|
|
||||||
if (storage.isPacked())
|
|
||||||
db.removePackedRef(ref.getName());
|
|
||||||
|
|
||||||
final int levels = count(ref.getName(), '/') - 2;
|
|
||||||
|
|
||||||
// Delete logs _before_ unlocking
|
|
||||||
final File gitDir = db.getRepository().getDirectory();
|
|
||||||
final File logDir = new File(gitDir, Constants.LOGS);
|
|
||||||
deleteFileAndEmptyDir(new File(logDir, ref.getName()), levels);
|
|
||||||
|
|
||||||
// We have to unlock before (maybe) deleting the parent directories
|
|
||||||
lock.unlock();
|
|
||||||
if (storage.isLoose())
|
|
||||||
deleteFileAndEmptyDir(looseFile, levels);
|
|
||||||
db.uncacheRef(ref.getName());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteFileAndEmptyDir(final File file, final int depth)
|
|
||||||
throws IOException {
|
|
||||||
if (file.isFile()) {
|
|
||||||
if (!file.delete())
|
|
||||||
throw new IOException("File cannot be deleted: " + file);
|
|
||||||
File dir = file.getParentFile();
|
|
||||||
for (int i = 0; i < depth; ++i) {
|
|
||||||
if (!dir.delete())
|
|
||||||
break; // ignore problem here
|
|
||||||
dir = dir.getParentFile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateStore newUpdateStore() {
|
|
||||||
return new UpdateStore();
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteStore newDeleteStore() {
|
|
||||||
return new DeleteStore();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deleteEmptyDir(File dir, int depth) {
|
|
||||||
for (; depth > 0 && dir != null; depth--) {
|
|
||||||
if (dir.exists() && !dir.delete())
|
|
||||||
break;
|
|
||||||
dir = dir.getParentFile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int count(final String s, final char c) {
|
|
||||||
int count = 0;
|
|
||||||
for (int p = s.indexOf(c); p >= 0; p = s.indexOf(c, p + 1)) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,10 @@
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.util.RefList;
|
||||||
|
import org.eclipse.jgit.util.RefMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes out refs to the {@link Constants#INFO_REFS} and
|
* Writes out refs to the {@link Constants#INFO_REFS} and
|
||||||
|
@ -70,6 +74,22 @@ public RefWriter(Collection<Ref> refs) {
|
||||||
this.refs = RefComparator.sort(refs);
|
this.refs = RefComparator.sort(refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param refs
|
||||||
|
* the complete set of references. This should have been computed
|
||||||
|
* by applying updates to the advertised refs already discovered.
|
||||||
|
*/
|
||||||
|
public RefWriter(Map<String, Ref> refs) {
|
||||||
|
if (refs instanceof RefMap)
|
||||||
|
this.refs = refs.values();
|
||||||
|
else
|
||||||
|
this.refs = RefComparator.sort(refs.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
RefWriter(RefList<Ref> list) {
|
||||||
|
this.refs = list.asList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuild the {@link Constants#INFO_REFS}.
|
* Rebuild the {@link Constants#INFO_REFS}.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -85,7 +105,7 @@ public void writeInfoRefs() throws IOException {
|
||||||
final StringWriter w = new StringWriter();
|
final StringWriter w = new StringWriter();
|
||||||
final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
|
final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
|
||||||
for (final Ref r : refs) {
|
for (final Ref r : refs) {
|
||||||
if (Constants.HEAD.equals(r.getOrigName())) {
|
if (Constants.HEAD.equals(r.getName())) {
|
||||||
// Historically HEAD has never been published through
|
// Historically HEAD has never been published through
|
||||||
// the INFO_REFS file. This is a mistake, but its the
|
// the INFO_REFS file. This is a mistake, but its the
|
||||||
// way things are.
|
// way things are.
|
||||||
|
@ -121,19 +141,18 @@ public void writeInfoRefs() throws IOException {
|
||||||
*/
|
*/
|
||||||
public void writePackedRefs() throws IOException {
|
public void writePackedRefs() throws IOException {
|
||||||
boolean peeled = false;
|
boolean peeled = false;
|
||||||
|
|
||||||
for (final Ref r : refs) {
|
for (final Ref r : refs) {
|
||||||
if (r.getStorage() != Ref.Storage.PACKED)
|
if (r.getStorage().isPacked() && r.isPeeled()) {
|
||||||
continue;
|
|
||||||
if (r.getPeeledObjectId() != null)
|
|
||||||
peeled = true;
|
peeled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringWriter w = new StringWriter();
|
final StringWriter w = new StringWriter();
|
||||||
if (peeled) {
|
if (peeled) {
|
||||||
w.write("# pack-refs with:");
|
w.write(RefDirectory.PACKED_REFS_HEADER);
|
||||||
if (peeled)
|
if (peeled)
|
||||||
w.write(" peeled");
|
w.write(RefDirectory.PACKED_REFS_PEELED);
|
||||||
w.write('\n');
|
w.write('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
|
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
|
||||||
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
|
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
@ -45,10 +46,7 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.lib;
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -223,7 +221,7 @@ public Repository(final File d, final File workTree, final File objectDir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refs = new RefDatabase(this);
|
refs = new RefDirectory(this);
|
||||||
if (objectDir != null)
|
if (objectDir != null)
|
||||||
objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""),
|
objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""),
|
||||||
alternateObjectDir);
|
alternateObjectDir);
|
||||||
|
@ -278,8 +276,10 @@ public void create(boolean bare) throws IOException {
|
||||||
objectDatabase.create();
|
objectDatabase.create();
|
||||||
|
|
||||||
new File(gitDir, "branches").mkdir();
|
new File(gitDir, "branches").mkdir();
|
||||||
final String master = Constants.R_HEADS + Constants.MASTER;
|
|
||||||
refs.link(Constants.HEAD, master);
|
RefUpdate head = updateRef(Constants.HEAD);
|
||||||
|
head.disableRefLog();
|
||||||
|
head.link(Constants.R_HEADS + Constants.MASTER);
|
||||||
|
|
||||||
cfg.setInt("core", null, "repositoryformatversion", 0);
|
cfg.setInt("core", null, "repositoryformatversion", 0);
|
||||||
cfg.setBoolean("core", null, "filemode", true);
|
cfg.setBoolean("core", null, "filemode", true);
|
||||||
|
@ -310,6 +310,11 @@ public ObjectDatabase getObjectDatabase() {
|
||||||
return objectDatabase;
|
return objectDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return the reference database which stores the reference namespace. */
|
||||||
|
public RefDatabase getRefDatabase() {
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the configuration of this repository
|
* @return the configuration of this repository
|
||||||
*/
|
*/
|
||||||
|
@ -591,7 +596,7 @@ public Tag mapTag(final String refName, final ObjectId id) throws IOException {
|
||||||
* to the base ref, as the symbolic ref could not be read.
|
* to the base ref, as the symbolic ref could not be read.
|
||||||
*/
|
*/
|
||||||
public RefUpdate updateRef(final String ref) throws IOException {
|
public RefUpdate updateRef(final String ref) throws IOException {
|
||||||
return refs.newUpdate(ref);
|
return updateRef(ref, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -862,7 +867,7 @@ else if (item.equals("")) {
|
||||||
private ObjectId resolveSimple(final String revstr) throws IOException {
|
private ObjectId resolveSimple(final String revstr) throws IOException {
|
||||||
if (ObjectId.isId(revstr))
|
if (ObjectId.isId(revstr))
|
||||||
return ObjectId.fromString(revstr);
|
return ObjectId.fromString(revstr);
|
||||||
final Ref r = refs.readRef(revstr);
|
final Ref r = refs.getRef(revstr);
|
||||||
return r != null ? r.getObjectId() : null;
|
return r != null ? r.getObjectId() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,8 +880,10 @@ public void incrementOpen() {
|
||||||
* Close all resources used by this repository
|
* Close all resources used by this repository
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
if (useCnt.decrementAndGet() == 0)
|
if (useCnt.decrementAndGet() == 0) {
|
||||||
objectDatabase.close();
|
objectDatabase.close();
|
||||||
|
refs.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -894,70 +901,53 @@ public void openPack(final File pack, final File idx) throws IOException {
|
||||||
objectDatabase.openPack(pack, idx);
|
objectDatabase.openPack(pack, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a symref (e.g. HEAD) to disk
|
|
||||||
*
|
|
||||||
* @param name symref name
|
|
||||||
* @param target pointed to ref
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void writeSymref(final String name, final String target)
|
|
||||||
throws IOException {
|
|
||||||
refs.link(name, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Repository[" + getDirectory() + "]";
|
return "Repository[" + getDirectory() + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return name of current branch
|
* Get the name of the reference that {@code HEAD} points to.
|
||||||
|
* <p>
|
||||||
|
* This is essentially the same as doing:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* return getRef(Constants.HEAD).getTarget().getName()
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Except when HEAD is detached, in which case this method returns the
|
||||||
|
* current ObjectId in hexadecimal string format.
|
||||||
|
*
|
||||||
|
* @return name of current branch (for example {@code refs/heads/master}) or
|
||||||
|
* an ObjectId in hex format if the current branch is detached.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public String getFullBranch() throws IOException {
|
public String getFullBranch() throws IOException {
|
||||||
final File ptr = new File(getDirectory(),Constants.HEAD);
|
Ref head = getRef(Constants.HEAD);
|
||||||
final BufferedReader br = new BufferedReader(new FileReader(ptr));
|
if (head == null)
|
||||||
String ref;
|
return null;
|
||||||
try {
|
if (head.isSymbolic())
|
||||||
ref = br.readLine();
|
return head.getTarget().getName();
|
||||||
} finally {
|
if (head.getObjectId() != null)
|
||||||
br.close();
|
return head.getObjectId().name();
|
||||||
}
|
return null;
|
||||||
if (ref.startsWith("ref: "))
|
|
||||||
ref = ref.substring(5);
|
|
||||||
return ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return name of current branch.
|
* Get the short name of the current branch that {@code HEAD} points to.
|
||||||
|
* <p>
|
||||||
|
* This is essentially the same as {@link #getFullBranch()}, except the
|
||||||
|
* leading prefix {@code refs/heads/} is removed from the reference before
|
||||||
|
* it is returned to the caller.
|
||||||
|
*
|
||||||
|
* @return name of current branch (for example {@code master}), or an
|
||||||
|
* ObjectId in hex format if the current branch is detached.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public String getBranch() throws IOException {
|
public String getBranch() throws IOException {
|
||||||
try {
|
String name = getFullBranch();
|
||||||
final File ptr = new File(getDirectory(), Constants.HEAD);
|
if (name != null)
|
||||||
final BufferedReader br = new BufferedReader(new FileReader(ptr));
|
return shortenRefName(name);
|
||||||
String ref;
|
return name;
|
||||||
try {
|
|
||||||
ref = br.readLine();
|
|
||||||
} finally {
|
|
||||||
br.close();
|
|
||||||
}
|
|
||||||
if (ref.startsWith("ref: "))
|
|
||||||
ref = ref.substring(5);
|
|
||||||
if (ref.startsWith("refs/heads/"))
|
|
||||||
ref = ref.substring(11);
|
|
||||||
return ref;
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
final File ptr = new File(getDirectory(),"head-name");
|
|
||||||
final BufferedReader br = new BufferedReader(new FileReader(ptr));
|
|
||||||
String ref;
|
|
||||||
try {
|
|
||||||
ref = br.readLine();
|
|
||||||
} finally {
|
|
||||||
br.close();
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -971,26 +961,35 @@ public String getBranch() throws IOException {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Ref getRef(final String name) throws IOException {
|
public Ref getRef(final String name) throws IOException {
|
||||||
return refs.readRef(name);
|
return refs.getRef(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all known refs (heads, tags, remotes).
|
* @return mutable map of all known refs (heads, tags, remotes).
|
||||||
*/
|
*/
|
||||||
public Map<String, Ref> getAllRefs() {
|
public Map<String, Ref> getAllRefs() {
|
||||||
return refs.getAllRefs();
|
try {
|
||||||
|
return refs.getRefs(RefDatabase.ALL);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return new HashMap<String, Ref>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all tags; key is short tag name ("v1.0") and value of the entry
|
* @return mutable map of all tags; key is short tag name ("v1.0") and value
|
||||||
* contains the ref with the full tag name ("refs/tags/v1.0").
|
* of the entry contains the ref with the full tag name
|
||||||
|
* ("refs/tags/v1.0").
|
||||||
*/
|
*/
|
||||||
public Map<String, Ref> getTags() {
|
public Map<String, Ref> getTags() {
|
||||||
return refs.getTags();
|
try {
|
||||||
|
return refs.getRefs(Constants.R_TAGS);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return new HashMap<String, Ref>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peel a possibly unpeeled ref and updates it.
|
* Peel a possibly unpeeled reference to an annotated tag.
|
||||||
* <p>
|
* <p>
|
||||||
* If the ref cannot be peeled (as it does not refer to an annotated tag)
|
* If the ref cannot be peeled (as it does not refer to an annotated tag)
|
||||||
* the peeled id stays null, but {@link Ref#isPeeled()} will be true.
|
* the peeled id stays null, but {@link Ref#isPeeled()} will be true.
|
||||||
|
@ -1003,7 +1002,14 @@ public Map<String, Ref> getTags() {
|
||||||
* (or null).
|
* (or null).
|
||||||
*/
|
*/
|
||||||
public Ref peel(final Ref ref) {
|
public Ref peel(final Ref ref) {
|
||||||
return refs.peel(ref);
|
try {
|
||||||
|
return refs.peel(ref);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Historical accident; if the reference cannot be peeled due
|
||||||
|
// to some sort of repository access problem we claim that the
|
||||||
|
// same as if the reference was not an annotated tag.
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1013,8 +1019,7 @@ public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
|
||||||
Map<String, Ref> allRefs = getAllRefs();
|
Map<String, Ref> allRefs = getAllRefs();
|
||||||
Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
|
Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
|
||||||
for (Ref ref : allRefs.values()) {
|
for (Ref ref : allRefs.values()) {
|
||||||
if (!ref.isPeeled())
|
ref = peel(ref);
|
||||||
ref = peel(ref);
|
|
||||||
AnyObjectId target = ref.getPeeledObjectId();
|
AnyObjectId target = ref.getPeeledObjectId();
|
||||||
if (target == null)
|
if (target == null)
|
||||||
target = ref.getObjectId();
|
target = ref.getObjectId();
|
||||||
|
@ -1033,11 +1038,6 @@ public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clean up stale caches */
|
|
||||||
public void refreshFromDisk() {
|
|
||||||
refs.clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a representation of the index associated with this repo
|
* @return a representation of the index associated with this repo
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
@ -1115,7 +1115,7 @@ public static boolean isValidRefName(final String refName) {
|
||||||
final int len = refName.length();
|
final int len = refName.length();
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return false;
|
return false;
|
||||||
if (refName.endsWith(".lock"))
|
if (refName.endsWith(LockFile.SUFFIX))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int components = 1;
|
int components = 1;
|
||||||
|
@ -1233,20 +1233,17 @@ public static void removeAnyRepositoryChangedListener(final RepositoryListener l
|
||||||
allListeners.remove(l);
|
allListeners.remove(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fireRefsMaybeChanged() {
|
void fireRefsChanged() {
|
||||||
if (refs.lastRefModification != refs.lastNotifiedRefModification) {
|
final RefsChangedEvent event = new RefsChangedEvent(this);
|
||||||
refs.lastNotifiedRefModification = refs.lastRefModification;
|
List<RepositoryListener> all;
|
||||||
final RefsChangedEvent event = new RefsChangedEvent(this);
|
synchronized (listeners) {
|
||||||
List<RepositoryListener> all;
|
all = new ArrayList<RepositoryListener>(listeners);
|
||||||
synchronized (listeners) {
|
}
|
||||||
all = new ArrayList<RepositoryListener>(listeners);
|
synchronized (allListeners) {
|
||||||
}
|
all.addAll(allListeners);
|
||||||
synchronized (allListeners) {
|
}
|
||||||
all.addAll(allListeners);
|
for (final RepositoryListener l : all) {
|
||||||
}
|
l.refsChanged(event);
|
||||||
for (final RepositoryListener l : all) {
|
|
||||||
l.refsChanged(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1298,7 +1295,7 @@ public String shortenRefName(String refName) {
|
||||||
public ReflogReader getReflogReader(String refName) throws IOException {
|
public ReflogReader getReflogReader(String refName) throws IOException {
|
||||||
Ref ref = getRef(refName);
|
Ref ref = getRef(refName);
|
||||||
if (ref != null)
|
if (ref != null)
|
||||||
return new ReflogReader(this, ref.getOrigName());
|
return new ReflogReader(this, ref.getName());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference that indirectly points at another {@link Ref}.
|
||||||
|
* <p>
|
||||||
|
* A symbolic reference always derives its current value from the target
|
||||||
|
* reference.
|
||||||
|
*/
|
||||||
|
public class SymbolicRef implements Ref {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final Ref target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ref pairing.
|
||||||
|
*
|
||||||
|
* @param refName
|
||||||
|
* name of this ref.
|
||||||
|
* @param target
|
||||||
|
* the ref we reference and derive our value from.
|
||||||
|
*/
|
||||||
|
public SymbolicRef(String refName, Ref target) {
|
||||||
|
this.name = refName;
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSymbolic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref getLeaf() {
|
||||||
|
Ref dst = getTarget();
|
||||||
|
while (dst.isSymbolic())
|
||||||
|
dst = dst.getTarget();
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectId getObjectId() {
|
||||||
|
return getLeaf().getObjectId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Storage getStorage() {
|
||||||
|
return Storage.LOOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectId getPeeledObjectId() {
|
||||||
|
return getLeaf().getPeeledObjectId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPeeled() {
|
||||||
|
return getLeaf().isPeeled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder r = new StringBuilder();
|
||||||
|
r.append("SymbolicRef[");
|
||||||
|
Ref cur = this;
|
||||||
|
while (cur.isSymbolic()) {
|
||||||
|
r.append(cur.getName());
|
||||||
|
r.append(" -> ");
|
||||||
|
cur = cur.getTarget();
|
||||||
|
}
|
||||||
|
r.append(cur.getName());
|
||||||
|
r.append('=');
|
||||||
|
r.append(ObjectId.toString(cur.getObjectId()));
|
||||||
|
r.append("]");
|
||||||
|
return r.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,7 @@
|
||||||
import org.eclipse.jgit.errors.PackProtocolException;
|
import org.eclipse.jgit.errors.PackProtocolException;
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.util.io.InterruptTimer;
|
import org.eclipse.jgit.util.io.InterruptTimer;
|
||||||
|
@ -209,11 +210,11 @@ private void readAdvertisedRefsImpl() throws IOException {
|
||||||
if (prior.getPeeledObjectId() != null)
|
if (prior.getPeeledObjectId() != null)
|
||||||
throw duplicateAdvertisement(name + "^{}");
|
throw duplicateAdvertisement(name + "^{}");
|
||||||
|
|
||||||
avail.put(name, new Ref(Ref.Storage.NETWORK, name, prior
|
avail.put(name, new ObjectIdRef.PeeledTag(
|
||||||
.getObjectId(), id, true));
|
Ref.Storage.NETWORK, name, prior.getObjectId(), id));
|
||||||
} else {
|
} else {
|
||||||
final Ref prior;
|
final Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
|
||||||
prior = avail.put(name, new Ref(Ref.Storage.NETWORK, name, id));
|
Ref.Storage.NETWORK, name, id));
|
||||||
if (prior != null)
|
if (prior != null)
|
||||||
throw duplicateAdvertisement(name);
|
throw duplicateAdvertisement(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
|
* Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
|
||||||
* Copyright (C) 2008-2009, Google Inc.
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* Copyright (C) 2009, Sasa Zivkov <sasa.zivkov@sap.com>
|
* Copyright (C) 2009, Sasa Zivkov <sasa.zivkov@sap.com>
|
||||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
|
||||||
|
@ -65,6 +65,7 @@
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.PackLock;
|
import org.eclipse.jgit.lib.PackLock;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
@ -140,8 +141,8 @@ private void readBundleV2() throws IOException {
|
||||||
|
|
||||||
final String name = line.substring(41, line.length());
|
final String name = line.substring(41, line.length());
|
||||||
final ObjectId id = ObjectId.fromString(line.substring(0, 40));
|
final ObjectId id = ObjectId.fromString(line.substring(0, 40));
|
||||||
final Ref prior = avail.put(name, new Ref(Ref.Storage.NETWORK,
|
final Ref prior = avail.put(name, new ObjectIdRef.Unpeeled(
|
||||||
name, id));
|
Ref.Storage.NETWORK, name, id));
|
||||||
if (prior != null)
|
if (prior != null)
|
||||||
throw duplicateAdvertisement(name);
|
throw duplicateAdvertisement(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2009, Google Inc.
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -52,7 +52,6 @@
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -590,10 +589,10 @@ public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
|
||||||
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
|
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
|
||||||
if (allowOfsDelta)
|
if (allowOfsDelta)
|
||||||
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
|
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
|
||||||
refs = new HashMap<String, Ref>(db.getAllRefs());
|
refs = db.getAllRefs();
|
||||||
final Ref head = refs.remove(Constants.HEAD);
|
final Ref head = refs.remove(Constants.HEAD);
|
||||||
adv.send(refs.values());
|
adv.send(refs);
|
||||||
if (head != null && head.getName().equals(head.getOrigName()))
|
if (!head.isSymbolic())
|
||||||
adv.advertiseHave(head.getObjectId());
|
adv.advertiseHave(head.getObjectId());
|
||||||
adv.includeAdditionalHaves();
|
adv.includeAdditionalHaves();
|
||||||
if (adv.isEmpty())
|
if (adv.isEmpty())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2009, Google Inc.
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -44,9 +44,10 @@
|
||||||
package org.eclipse.jgit.transport;
|
package org.eclipse.jgit.transport;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.AlternateRepositoryDatabase;
|
import org.eclipse.jgit.lib.AlternateRepositoryDatabase;
|
||||||
import org.eclipse.jgit.lib.AnyObjectId;
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
import org.eclipse.jgit.revwalk.RevTag;
|
import org.eclipse.jgit.revwalk.RevTag;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.util.RefMap;
|
||||||
|
|
||||||
/** Support for the start of {@link UploadPack} and {@link ReceivePack}. */
|
/** Support for the start of {@link UploadPack} and {@link ReceivePack}. */
|
||||||
public abstract class RefAdvertiser {
|
public abstract class RefAdvertiser {
|
||||||
|
@ -122,7 +124,7 @@ public void init(final RevWalk protoWalk, final RevFlag advertisedFlag) {
|
||||||
* <p>
|
* <p>
|
||||||
* This method must be invoked prior to any of the following:
|
* This method must be invoked prior to any of the following:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #send(Collection)}
|
* <li>{@link #send(Map)}
|
||||||
* <li>{@link #advertiseHave(AnyObjectId)}
|
* <li>{@link #advertiseHave(AnyObjectId)}
|
||||||
* <li>{@link #includeAdditionalHaves()}
|
* <li>{@link #includeAdditionalHaves()}
|
||||||
* </ul>
|
* </ul>
|
||||||
|
@ -140,7 +142,7 @@ public void setDerefTags(final boolean deref) {
|
||||||
* <p>
|
* <p>
|
||||||
* This method must be invoked prior to any of the following:
|
* This method must be invoked prior to any of the following:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #send(Collection)}
|
* <li>{@link #send(Map)}
|
||||||
* <li>{@link #advertiseHave(AnyObjectId)}
|
* <li>{@link #advertiseHave(AnyObjectId)}
|
||||||
* <li>{@link #includeAdditionalHaves()}
|
* <li>{@link #includeAdditionalHaves()}
|
||||||
* </ul>
|
* </ul>
|
||||||
|
@ -160,23 +162,30 @@ public void advertiseCapability(String name) {
|
||||||
*
|
*
|
||||||
* @param refs
|
* @param refs
|
||||||
* zero or more refs to format for the client. The collection is
|
* zero or more refs to format for the client. The collection is
|
||||||
* copied and sorted before display and therefore may appear in
|
* sorted before display if necessary, and therefore may appear
|
||||||
* any order.
|
* in any order.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the underlying output stream failed to write out an
|
* the underlying output stream failed to write out an
|
||||||
* advertisement record.
|
* advertisement record.
|
||||||
*/
|
*/
|
||||||
public void send(final Collection<Ref> refs) throws IOException {
|
public void send(final Map<String, Ref> refs) throws IOException {
|
||||||
for (final Ref r : RefComparator.sort(refs)) {
|
for (final Ref r : getSortedRefs(refs)) {
|
||||||
final RevObject obj = parseAnyOrNull(r.getObjectId());
|
final RevObject obj = parseAnyOrNull(r.getObjectId());
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
advertiseAny(obj, r.getOrigName());
|
advertiseAny(obj, r.getName());
|
||||||
if (derefTags && obj instanceof RevTag)
|
if (derefTags && obj instanceof RevTag)
|
||||||
advertiseTag((RevTag) obj, r.getOrigName() + "^{}");
|
advertiseTag((RevTag) obj, r.getName() + "^{}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Iterable<Ref> getSortedRefs(Map<String, Ref> all) {
|
||||||
|
if (all instanceof RefMap
|
||||||
|
|| (all instanceof SortedMap && ((SortedMap) all).comparator() == null))
|
||||||
|
return all.values();
|
||||||
|
return RefComparator.sort(all.values());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advertise one object is available using the magic {@code .have}.
|
* Advertise one object is available using the magic {@code .have}.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -61,9 +61,11 @@
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.lib.SymbolicRef;
|
||||||
import org.eclipse.jgit.lib.Ref.Storage;
|
import org.eclipse.jgit.lib.Ref.Storage;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
|
|
||||||
|
@ -305,16 +307,15 @@ private Ref readRef(final TreeMap<String, Ref> avail, final String rn)
|
||||||
if (r == null)
|
if (r == null)
|
||||||
r = readRef(avail, target);
|
r = readRef(avail, target);
|
||||||
if (r == null)
|
if (r == null)
|
||||||
return null;
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, target, null);
|
||||||
r = new Ref(r.getStorage(), rn, r.getObjectId(), r
|
r = new SymbolicRef(rn, r);
|
||||||
.getPeeledObjectId(), r.isPeeled());
|
|
||||||
avail.put(r.getName(), r);
|
avail.put(r.getName(), r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectId.isId(s)) {
|
if (ObjectId.isId(s)) {
|
||||||
final Ref r = new Ref(loose(avail.get(rn)), rn, ObjectId
|
final Ref r = new ObjectIdRef.Unpeeled(loose(avail.get(rn)),
|
||||||
.fromString(s));
|
rn, ObjectId.fromString(s));
|
||||||
avail.put(r.getName(), r);
|
avail.put(r.getName(), r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,9 +80,12 @@
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefDirectory;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.lib.SymbolicRef;
|
||||||
import org.eclipse.jgit.lib.Config.SectionParser;
|
import org.eclipse.jgit.lib.Config.SectionParser;
|
||||||
import org.eclipse.jgit.util.HttpSupport;
|
import org.eclipse.jgit.util.HttpSupport;
|
||||||
import org.eclipse.jgit.util.IO;
|
import org.eclipse.jgit.util.IO;
|
||||||
|
@ -240,16 +243,17 @@ private FetchConnection newDumbConnection(InputStream in)
|
||||||
br = toBufferedReader(openInputStream(conn));
|
br = toBufferedReader(openInputStream(conn));
|
||||||
try {
|
try {
|
||||||
String line = br.readLine();
|
String line = br.readLine();
|
||||||
if (line != null && line.startsWith("ref: ")) {
|
if (line != null && line.startsWith(RefDirectory.SYMREF)) {
|
||||||
Ref src = refs.get(line.substring(5));
|
String target = line.substring(RefDirectory.SYMREF.length());
|
||||||
if (src != null) {
|
Ref r = refs.get(target);
|
||||||
refs.put(Constants.HEAD, new Ref(
|
if (r == null)
|
||||||
Ref.Storage.NETWORK, Constants.HEAD, src
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, target, null);
|
||||||
.getName(), src.getObjectId()));
|
r = new SymbolicRef(Constants.HEAD, r);
|
||||||
}
|
refs.put(r.getName(), r);
|
||||||
} else if (line != null && ObjectId.isId(line)) {
|
} else if (line != null && ObjectId.isId(line)) {
|
||||||
refs.put(Constants.HEAD, new Ref(Ref.Storage.NETWORK,
|
Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK,
|
||||||
Constants.HEAD, ObjectId.fromString(line)));
|
Constants.HEAD, ObjectId.fromString(line));
|
||||||
|
refs.put(r.getName(), r);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
br.close();
|
br.close();
|
||||||
|
@ -527,10 +531,11 @@ Map<String, Ref> readAdvertisedImpl(final BufferedReader br)
|
||||||
if (prior.getPeeledObjectId() != null)
|
if (prior.getPeeledObjectId() != null)
|
||||||
throw duplicateAdvertisement(name + "^{}");
|
throw duplicateAdvertisement(name + "^{}");
|
||||||
|
|
||||||
avail.put(name, new Ref(Ref.Storage.NETWORK, name, prior
|
avail.put(name, new ObjectIdRef.PeeledTag(
|
||||||
.getObjectId(), id, true));
|
Ref.Storage.NETWORK, name,
|
||||||
|
prior.getObjectId(), id));
|
||||||
} else {
|
} else {
|
||||||
final Ref prior = avail.put(name, new Ref(
|
Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
|
||||||
Ref.Storage.NETWORK, name, id));
|
Ref.Storage.NETWORK, name, id));
|
||||||
if (prior != null)
|
if (prior != null)
|
||||||
throw duplicateAdvertisement(name);
|
throw duplicateAdvertisement(name);
|
||||||
|
|
|
@ -59,9 +59,11 @@
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.lib.SymbolicRef;
|
||||||
import org.eclipse.jgit.lib.Ref.Storage;
|
import org.eclipse.jgit.lib.Ref.Storage;
|
||||||
|
|
||||||
import com.jcraft.jsch.Channel;
|
import com.jcraft.jsch.Channel;
|
||||||
|
@ -389,21 +391,20 @@ private Ref readRef(final TreeMap<String, Ref> avail,
|
||||||
throw new TransportException("Empty ref: " + name);
|
throw new TransportException("Empty ref: " + name);
|
||||||
|
|
||||||
if (line.startsWith("ref: ")) {
|
if (line.startsWith("ref: ")) {
|
||||||
final String p = line.substring("ref: ".length());
|
final String target = line.substring("ref: ".length());
|
||||||
Ref r = readRef(avail, ROOT_DIR + p, p);
|
Ref r = avail.get(target);
|
||||||
if (r == null)
|
if (r == null)
|
||||||
r = avail.get(p);
|
r = readRef(avail, ROOT_DIR + target, target);
|
||||||
if (r != null) {
|
if (r == null)
|
||||||
r = new Ref(loose(r), name, r.getObjectId(), r
|
r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, target, null);
|
||||||
.getPeeledObjectId(), true);
|
r = new SymbolicRef(name, r);
|
||||||
avail.put(name, r);
|
avail.put(r.getName(), r);
|
||||||
}
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectId.isId(line)) {
|
if (ObjectId.isId(line)) {
|
||||||
final Ref r = new Ref(loose(avail.get(name)), name, ObjectId
|
final Ref r = new ObjectIdRef.Unpeeled(loose(avail.get(name)),
|
||||||
.fromString(line));
|
name, ObjectId.fromString(line));
|
||||||
avail.put(r.getName(), r);
|
avail.put(r.getName(), r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2009, Google Inc.
|
* Copyright (C) 2008-2010, Google Inc.
|
||||||
* and other copyright owners as documented in the project's IP log.
|
* and other copyright owners as documented in the project's IP log.
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available
|
* This program and the accompanying materials are made available
|
||||||
|
@ -330,7 +330,7 @@ public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
|
||||||
adv.advertiseCapability(OPTION_NO_PROGRESS);
|
adv.advertiseCapability(OPTION_NO_PROGRESS);
|
||||||
adv.setDerefTags(true);
|
adv.setDerefTags(true);
|
||||||
refs = db.getAllRefs();
|
refs = db.getAllRefs();
|
||||||
adv.send(refs.values());
|
adv.send(refs);
|
||||||
adv.end();
|
adv.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
import org.eclipse.jgit.lib.AnyObjectId;
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.PackWriter;
|
import org.eclipse.jgit.lib.PackWriter;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
@ -324,8 +325,8 @@ private void deleteCommand(final RemoteRefUpdate u) {
|
||||||
private void updateCommand(final RemoteRefUpdate u) {
|
private void updateCommand(final RemoteRefUpdate u) {
|
||||||
try {
|
try {
|
||||||
dest.writeRef(u.getRemoteName(), u.getNewObjectId());
|
dest.writeRef(u.getRemoteName(), u.getNewObjectId());
|
||||||
newRefs.put(u.getRemoteName(), new Ref(Storage.LOOSE, u
|
newRefs.put(u.getRemoteName(), new ObjectIdRef.Unpeeled(
|
||||||
.getRemoteName(), u.getNewObjectId()));
|
Storage.LOOSE, u.getRemoteName(), u.getNewObjectId()));
|
||||||
u.setStatus(Status.OK);
|
u.setStatus(Status.OK);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
u.setStatus(Status.REJECTED_OTHER_REASON);
|
u.setStatus(Status.REJECTED_OTHER_REASON);
|
||||||
|
|
|
@ -57,8 +57,10 @@
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefDirectory;
|
||||||
import org.eclipse.jgit.util.IO;
|
import org.eclipse.jgit.util.IO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -433,18 +435,24 @@ protected void readPackedRefs(final Map<String, Ref> avail)
|
||||||
private void readPackedRefsImpl(final Map<String, Ref> avail,
|
private void readPackedRefsImpl(final Map<String, Ref> avail,
|
||||||
final BufferedReader br) throws IOException {
|
final BufferedReader br) throws IOException {
|
||||||
Ref last = null;
|
Ref last = null;
|
||||||
|
boolean peeled = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
String line = br.readLine();
|
String line = br.readLine();
|
||||||
if (line == null)
|
if (line == null)
|
||||||
break;
|
break;
|
||||||
if (line.charAt(0) == '#')
|
if (line.charAt(0) == '#') {
|
||||||
|
if (line.startsWith(RefDirectory.PACKED_REFS_HEADER)) {
|
||||||
|
line = line.substring(RefDirectory.PACKED_REFS_HEADER.length());
|
||||||
|
peeled = line.contains(RefDirectory.PACKED_REFS_PEELED);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
if (line.charAt(0) == '^') {
|
if (line.charAt(0) == '^') {
|
||||||
if (last == null)
|
if (last == null)
|
||||||
throw new TransportException("Peeled line before ref.");
|
throw new TransportException("Peeled line before ref.");
|
||||||
final ObjectId id = ObjectId.fromString(line.substring(1));
|
final ObjectId id = ObjectId.fromString(line.substring(1));
|
||||||
last = new Ref(Ref.Storage.PACKED, last.getName(), last
|
last = new ObjectIdRef.PeeledTag(Ref.Storage.PACKED, last
|
||||||
.getObjectId(), id, true);
|
.getName(), last.getObjectId(), id);
|
||||||
avail.put(last.getName(), last);
|
avail.put(last.getName(), last);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -454,7 +462,10 @@ private void readPackedRefsImpl(final Map<String, Ref> avail,
|
||||||
throw new TransportException("Unrecognized ref: " + line);
|
throw new TransportException("Unrecognized ref: " + line);
|
||||||
final ObjectId id = ObjectId.fromString(line.substring(0, sp));
|
final ObjectId id = ObjectId.fromString(line.substring(0, sp));
|
||||||
final String name = line.substring(sp + 1);
|
final String name = line.substring(sp + 1);
|
||||||
last = new Ref(Ref.Storage.PACKED, name, id);
|
if (peeled)
|
||||||
|
last = new ObjectIdRef.PeeledNonTag(Ref.Storage.PACKED, name, id);
|
||||||
|
else
|
||||||
|
last = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, name, id);
|
||||||
avail.put(last.getName(), last);
|
avail.put(last.getName(), last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,438 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized variant of an ArrayList to support a {@code RefDatabase}.
|
||||||
|
* <p>
|
||||||
|
* This list is a hybrid of a Map<String,Ref> and of a List<Ref>. It
|
||||||
|
* tracks reference instances by name by keeping them sorted and performing
|
||||||
|
* binary search to locate an entry. Lookup time is O(log N), but addition and
|
||||||
|
* removal is O(N + log N) due to the list expansion or contraction costs.
|
||||||
|
* <p>
|
||||||
|
* This list type is copy-on-write. Mutation methods return a new copy of the
|
||||||
|
* list, leaving {@code this} unmodified. As a result we cannot easily implement
|
||||||
|
* the {@link java.util.List} interface contract.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of reference being stored in the collection.
|
||||||
|
*/
|
||||||
|
public class RefList<T extends Ref> implements Iterable<Ref> {
|
||||||
|
private static final RefList<Ref> EMPTY = new RefList<Ref>(new Ref[0], 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an empty unmodifiable reference list.
|
||||||
|
* @param <T>
|
||||||
|
* the type of reference being stored in the collection.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Ref> RefList<T> emptyList() {
|
||||||
|
return (RefList<T>) EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Ref[] list;
|
||||||
|
|
||||||
|
private final int cnt;
|
||||||
|
|
||||||
|
RefList(Ref[] list, int cnt) {
|
||||||
|
this.list = list;
|
||||||
|
this.cnt = cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize this list to use the same backing array as another list.
|
||||||
|
*
|
||||||
|
* @param src
|
||||||
|
* the source list.
|
||||||
|
*/
|
||||||
|
protected RefList(RefList<T> src) {
|
||||||
|
this.list = src.list;
|
||||||
|
this.cnt = src.cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<Ref> iterator() {
|
||||||
|
return new Iterator<Ref>() {
|
||||||
|
private int idx;
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return idx < cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref next() {
|
||||||
|
if (idx < cnt)
|
||||||
|
return list[idx++];
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return this cast as an immutable, standard {@link java.util.List}. */
|
||||||
|
public final List<Ref> asList() {
|
||||||
|
final List<Ref> r = Arrays.asList(list).subList(0, cnt);
|
||||||
|
return Collections.unmodifiableList(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return number of items in this list. */
|
||||||
|
public final int size() {
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return true if the size of this list is 0. */
|
||||||
|
public final boolean isEmpty() {
|
||||||
|
return cnt == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate an entry by name.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the reference to find.
|
||||||
|
* @return the index the reference is at. If the entry is not present
|
||||||
|
* returns a negative value. The insertion position for the given
|
||||||
|
* name can be computed from {@code -(index + 1)}.
|
||||||
|
*/
|
||||||
|
public final int find(String name) {
|
||||||
|
int high = cnt;
|
||||||
|
if (high == 0)
|
||||||
|
return -1;
|
||||||
|
int low = 0;
|
||||||
|
do {
|
||||||
|
final int mid = (low + high) >>> 1;
|
||||||
|
final int cmp = RefComparator.compareTo(list[mid], name);
|
||||||
|
if (cmp < 0)
|
||||||
|
low = mid + 1;
|
||||||
|
else if (cmp == 0)
|
||||||
|
return mid;
|
||||||
|
else
|
||||||
|
high = mid;
|
||||||
|
} while (low < high);
|
||||||
|
return -(low + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a reference is present.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* name of the reference to find.
|
||||||
|
* @return true if the reference is present; false if it is not.
|
||||||
|
*/
|
||||||
|
public final boolean contains(String name) {
|
||||||
|
return 0 <= find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a reference object by name.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the reference.
|
||||||
|
* @return the reference object; null if it does not exist in this list.
|
||||||
|
*/
|
||||||
|
public final T get(String name) {
|
||||||
|
int idx = find(name);
|
||||||
|
return 0 <= idx ? get(idx) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the reference at a particular index.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* the index to obtain. Must be {@code 0 <= idx < size()}.
|
||||||
|
* @return the reference value, never null.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final T get(int idx) {
|
||||||
|
return (T) list[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a builder initialized with the first {@code n} elements.
|
||||||
|
* <p>
|
||||||
|
* Copies the first {@code n} elements from this list into a new builder,
|
||||||
|
* which can be used by the caller to add additional elements.
|
||||||
|
*
|
||||||
|
* @param n
|
||||||
|
* the number of elements to copy.
|
||||||
|
* @return a new builder with the first {@code n} elements already added.
|
||||||
|
*/
|
||||||
|
public final Builder<T> copy(int n) {
|
||||||
|
Builder<T> r = new Builder<T>(Math.max(16, n));
|
||||||
|
r.addAll(list, 0, n);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a new copy of the list after changing one element.
|
||||||
|
* <p>
|
||||||
|
* This list instance is not affected by the replacement. Because this
|
||||||
|
* method copies the entire list, it runs in O(N) time.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* index of the element to change.
|
||||||
|
* @param ref
|
||||||
|
* the new value, must not be null.
|
||||||
|
* @return copy of this list, after replacing {@code idx} with {@code ref} .
|
||||||
|
*/
|
||||||
|
public final RefList<T> set(int idx, T ref) {
|
||||||
|
Ref[] newList = new Ref[cnt];
|
||||||
|
System.arraycopy(list, 0, newList, 0, cnt);
|
||||||
|
newList[idx] = ref;
|
||||||
|
return new RefList<T>(newList, cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an item at a specific index.
|
||||||
|
* <p>
|
||||||
|
* This list instance is not affected by the addition. Because this method
|
||||||
|
* copies the entire list, it runs in O(N) time.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* position to add the item at. If negative the method assumes it
|
||||||
|
* was a direct return value from {@link #find(String)} and will
|
||||||
|
* adjust it to the correct position.
|
||||||
|
* @param ref
|
||||||
|
* the new reference to insert.
|
||||||
|
* @return copy of this list, after making space for and adding {@code ref}.
|
||||||
|
*/
|
||||||
|
public final RefList<T> add(int idx, T ref) {
|
||||||
|
if (idx < 0)
|
||||||
|
idx = -(idx + 1);
|
||||||
|
|
||||||
|
Ref[] newList = new Ref[cnt + 1];
|
||||||
|
if (0 < idx)
|
||||||
|
System.arraycopy(list, 0, newList, 0, idx);
|
||||||
|
newList[idx] = ref;
|
||||||
|
if (idx < cnt)
|
||||||
|
System.arraycopy(list, idx, newList, idx + 1, cnt - idx);
|
||||||
|
return new RefList<T>(newList, cnt + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item at a specific index.
|
||||||
|
* <p>
|
||||||
|
* This list instance is not affected by the addition. Because this method
|
||||||
|
* copies the entire list, it runs in O(N) time.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* position to remove the item from.
|
||||||
|
* @return copy of this list, after making removing the item at {@code idx}.
|
||||||
|
*/
|
||||||
|
public final RefList<T> remove(int idx) {
|
||||||
|
if (cnt == 1)
|
||||||
|
return emptyList();
|
||||||
|
Ref[] newList = new Ref[cnt - 1];
|
||||||
|
if (0 < idx)
|
||||||
|
System.arraycopy(list, 0, newList, 0, idx);
|
||||||
|
if (idx + 1 < cnt)
|
||||||
|
System.arraycopy(list, idx + 1, newList, idx, cnt - (idx + 1));
|
||||||
|
return new RefList<T>(newList, cnt - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a reference, adding or replacing as necessary.
|
||||||
|
* <p>
|
||||||
|
* This list instance is not affected by the store. The correct position is
|
||||||
|
* determined, and the item is added if missing, or replaced if existing.
|
||||||
|
* Because this method copies the entire list, it runs in O(N + log N) time.
|
||||||
|
*
|
||||||
|
* @param ref
|
||||||
|
* the reference to store.
|
||||||
|
* @return copy of this list, after performing the addition or replacement.
|
||||||
|
*/
|
||||||
|
public final RefList<T> put(T ref) {
|
||||||
|
int idx = find(ref.getName());
|
||||||
|
if (0 <= idx)
|
||||||
|
return set(idx, ref);
|
||||||
|
return add(idx, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder r = new StringBuilder();
|
||||||
|
r.append('[');
|
||||||
|
if (cnt > 0) {
|
||||||
|
r.append(list[0]);
|
||||||
|
for (int i = 1; i < cnt; i++) {
|
||||||
|
r.append(", ");
|
||||||
|
r.append(list[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.append(']');
|
||||||
|
return r.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder to facilitate fast construction of an immutable RefList.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* type of reference being stored.
|
||||||
|
*/
|
||||||
|
public static class Builder<T extends Ref> {
|
||||||
|
private Ref[] list;
|
||||||
|
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
/** Create an empty list ready for items to be added. */
|
||||||
|
public Builder() {
|
||||||
|
this(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty list with at least the specified capacity.
|
||||||
|
*
|
||||||
|
* @param capacity
|
||||||
|
* the new capacity.
|
||||||
|
*/
|
||||||
|
public Builder(int capacity) {
|
||||||
|
list = new Ref[capacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return number of items in this builder's internal collection. */
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the reference at a particular index.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* the index to obtain. Must be {@code 0 <= idx < size()}.
|
||||||
|
* @return the reference value, never null.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get(int idx) {
|
||||||
|
return (T) list[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item at a specific index.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* position to remove the item from.
|
||||||
|
*/
|
||||||
|
public void remove(int idx) {
|
||||||
|
System.arraycopy(list, idx + 1, list, idx, size - (idx + 1));
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the reference to the end of the array.
|
||||||
|
* <p>
|
||||||
|
* References must be added in sort order, or the array must be sorted
|
||||||
|
* after additions are complete using {@link #sort()}.
|
||||||
|
*
|
||||||
|
* @param ref
|
||||||
|
*/
|
||||||
|
public void add(T ref) {
|
||||||
|
if (list.length == size) {
|
||||||
|
Ref[] n = new Ref[size * 2];
|
||||||
|
System.arraycopy(list, 0, n, 0, size);
|
||||||
|
list = n;
|
||||||
|
}
|
||||||
|
list[size++] = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all items from a source array.
|
||||||
|
* <p>
|
||||||
|
* References must be added in sort order, or the array must be sorted
|
||||||
|
* after additions are complete using {@link #sort()}.
|
||||||
|
*
|
||||||
|
* @param src
|
||||||
|
* the source array.
|
||||||
|
* @param off
|
||||||
|
* position within {@code src} to start copying from.
|
||||||
|
* @param cnt
|
||||||
|
* number of items to copy from {@code src}.
|
||||||
|
*/
|
||||||
|
public void addAll(Ref[] src, int off, int cnt) {
|
||||||
|
if (list.length < size + cnt) {
|
||||||
|
Ref[] n = new Ref[Math.max(size * 2, size + cnt)];
|
||||||
|
System.arraycopy(list, 0, n, 0, size);
|
||||||
|
list = n;
|
||||||
|
}
|
||||||
|
System.arraycopy(src, off, list, size, cnt);
|
||||||
|
size += cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a single existing element.
|
||||||
|
*
|
||||||
|
* @param idx
|
||||||
|
* index, must have already been added previously.
|
||||||
|
* @param ref
|
||||||
|
* the new reference.
|
||||||
|
*/
|
||||||
|
public void set(int idx, T ref) {
|
||||||
|
list[idx] = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sort the list's backing array in-place. */
|
||||||
|
public void sort() {
|
||||||
|
Arrays.sort(list, 0, size, RefComparator.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return an unmodifiable list using this collection's backing array. */
|
||||||
|
public RefList<T> toRefList() {
|
||||||
|
return new RefList<T>(list, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toRefList().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,423 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010, Google Inc.
|
||||||
|
* and other copyright owners as documented in the project's IP log.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available
|
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which
|
||||||
|
* accompanies this distribution, is reproduced below, and is
|
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials provided
|
||||||
|
* with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the
|
||||||
|
* names of its contributors may be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eclipse.jgit.util;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized Map to present a {@code RefDatabase} namespace.
|
||||||
|
* <p>
|
||||||
|
* Although not declared as a {@link java.util.SortedMap}, iterators from this
|
||||||
|
* map's projections always return references in {@link RefComparator} ordering.
|
||||||
|
* The map's internal representation is a sorted array of {@link Ref} objects,
|
||||||
|
* which means lookup and replacement is O(log N), while insertion and removal
|
||||||
|
* can be as expensive as O(N + log N) while the list expands or contracts.
|
||||||
|
* Since this is not a general map implementation, all entries must be keyed by
|
||||||
|
* the reference name.
|
||||||
|
* <p>
|
||||||
|
* This class is really intended as a helper for {@code RefDatabase}, which
|
||||||
|
* needs to perform a merge-join of three sorted {@link RefList}s in order to
|
||||||
|
* present the unified namespace of the packed-refs file, the loose refs/
|
||||||
|
* directory tree, and the resolved form of any symbolic references.
|
||||||
|
*/
|
||||||
|
public class RefMap extends AbstractMap<String, Ref> {
|
||||||
|
/**
|
||||||
|
* Prefix denoting the reference subspace this map contains.
|
||||||
|
* <p>
|
||||||
|
* All reference names in this map must start with this prefix. If the
|
||||||
|
* prefix is not the empty string, it must end with a '/'.
|
||||||
|
*/
|
||||||
|
private final String prefix;
|
||||||
|
|
||||||
|
/** Immutable collection of the packed references at construction time. */
|
||||||
|
private RefList<Ref> packed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable collection of the loose references at construction time.
|
||||||
|
* <p>
|
||||||
|
* If an entry appears here and in {@link #packed}, this entry must take
|
||||||
|
* precedence, as its more current. Symbolic references in this collection
|
||||||
|
* are typically unresolved, so they only tell us who their target is, but
|
||||||
|
* not the current value of the target.
|
||||||
|
*/
|
||||||
|
private RefList<Ref> loose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable collection of resolved symbolic references.
|
||||||
|
* <p>
|
||||||
|
* This collection contains only the symbolic references we were able to
|
||||||
|
* resolve at map construction time. Other loose references must be read
|
||||||
|
* from {@link #loose}. Every entry in this list must be matched by an entry
|
||||||
|
* in {@code loose}, otherwise it might be omitted by the map.
|
||||||
|
*/
|
||||||
|
private RefList<Ref> resolved;
|
||||||
|
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
private boolean sizeIsValid;
|
||||||
|
|
||||||
|
private Set<Entry<String, Ref>> entrySet;
|
||||||
|
|
||||||
|
/** Construct an empty map with a small initial capacity. */
|
||||||
|
public RefMap() {
|
||||||
|
prefix = "";
|
||||||
|
packed = RefList.emptyList();
|
||||||
|
loose = RefList.emptyList();
|
||||||
|
resolved = RefList.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a map to merge 3 collections together.
|
||||||
|
*
|
||||||
|
* @param prefix
|
||||||
|
* prefix used to slice the lists down. Only references whose
|
||||||
|
* names start with this prefix will appear to reside in the map.
|
||||||
|
* Must not be null, use {@code ""} (the empty string) to select
|
||||||
|
* all list items.
|
||||||
|
* @param packed
|
||||||
|
* items from the packed reference list, this is the last list
|
||||||
|
* searched.
|
||||||
|
* @param loose
|
||||||
|
* items from the loose reference list, this list overrides
|
||||||
|
* {@code packed} if a name appears in both.
|
||||||
|
* @param resolved
|
||||||
|
* resolved symbolic references. This list overrides the prior
|
||||||
|
* list {@code loose}, if an item appears in both. Items in this
|
||||||
|
* list <b>must</b> also appear in {@code loose}.
|
||||||
|
*/
|
||||||
|
public RefMap(String prefix, RefList<Ref> packed, RefList<Ref> loose,
|
||||||
|
RefList<Ref> resolved) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.packed = packed;
|
||||||
|
this.loose = loose;
|
||||||
|
this.resolved = resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object name) {
|
||||||
|
return get(name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ref get(Object key) {
|
||||||
|
String name = toRefName((String) key);
|
||||||
|
Ref ref = resolved.get(name);
|
||||||
|
if (ref == null)
|
||||||
|
ref = loose.get(name);
|
||||||
|
if (ref == null)
|
||||||
|
ref = packed.get(name);
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ref put(final String keyName, Ref value) {
|
||||||
|
String name = toRefName(keyName);
|
||||||
|
|
||||||
|
if (!name.equals(value.getName()))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
if (!resolved.isEmpty()) {
|
||||||
|
// Collapse the resolved list into the loose list so we
|
||||||
|
// can discard it and stop joining the two together.
|
||||||
|
for (Ref ref : resolved)
|
||||||
|
loose = loose.put(ref);
|
||||||
|
resolved = RefList.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = loose.find(name);
|
||||||
|
if (0 <= idx) {
|
||||||
|
Ref prior = loose.get(name);
|
||||||
|
loose = loose.set(idx, value);
|
||||||
|
return prior;
|
||||||
|
} else {
|
||||||
|
Ref prior = get(keyName);
|
||||||
|
loose = loose.add(idx, value);
|
||||||
|
sizeIsValid = false;
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ref remove(Object key) {
|
||||||
|
String name = toRefName((String) key);
|
||||||
|
Ref res = null;
|
||||||
|
int idx;
|
||||||
|
if (0 <= (idx = packed.find(name))) {
|
||||||
|
res = packed.get(name);
|
||||||
|
packed = packed.remove(idx);
|
||||||
|
sizeIsValid = false;
|
||||||
|
}
|
||||||
|
if (0 <= (idx = loose.find(name))) {
|
||||||
|
res = loose.get(name);
|
||||||
|
loose = loose.remove(idx);
|
||||||
|
sizeIsValid = false;
|
||||||
|
}
|
||||||
|
if (0 <= (idx = resolved.find(name))) {
|
||||||
|
res = resolved.get(name);
|
||||||
|
resolved = resolved.remove(idx);
|
||||||
|
sizeIsValid = false;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return entrySet().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<String, Ref>> entrySet() {
|
||||||
|
if (entrySet == null) {
|
||||||
|
entrySet = new AbstractSet<Entry<String, Ref>>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<String, Ref>> iterator() {
|
||||||
|
return new SetIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
if (!sizeIsValid) {
|
||||||
|
size = 0;
|
||||||
|
Iterator<?> i = entrySet().iterator();
|
||||||
|
for (; i.hasNext(); i.next())
|
||||||
|
size++;
|
||||||
|
sizeIsValid = true;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
if (sizeIsValid)
|
||||||
|
return 0 == size;
|
||||||
|
return !iterator().hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
packed = RefList.emptyList();
|
||||||
|
loose = RefList.emptyList();
|
||||||
|
resolved = RefList.emptyList();
|
||||||
|
size = 0;
|
||||||
|
sizeIsValid = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return entrySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder r = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
r.append('[');
|
||||||
|
for (Ref ref : values()) {
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
r.append(", ");
|
||||||
|
r.append(ref);
|
||||||
|
}
|
||||||
|
r.append(']');
|
||||||
|
return r.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toRefName(String name) {
|
||||||
|
if (0 < prefix.length())
|
||||||
|
name = prefix + name;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toMapKey(Ref ref) {
|
||||||
|
String name = ref.getName();
|
||||||
|
if (0 < prefix.length())
|
||||||
|
name = name.substring(prefix.length());
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SetIterator implements Iterator<Entry<String, Ref>> {
|
||||||
|
private int packedIdx;
|
||||||
|
|
||||||
|
private int looseIdx;
|
||||||
|
|
||||||
|
private int resolvedIdx;
|
||||||
|
|
||||||
|
private Entry<String, Ref> next;
|
||||||
|
|
||||||
|
SetIterator() {
|
||||||
|
if (0 < prefix.length()) {
|
||||||
|
packedIdx = -(packed.find(prefix) + 1);
|
||||||
|
looseIdx = -(loose.find(prefix) + 1);
|
||||||
|
resolvedIdx = -(resolved.find(prefix) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (next == null)
|
||||||
|
next = peek();
|
||||||
|
return next != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry<String, Ref> next() {
|
||||||
|
if (hasNext()) {
|
||||||
|
Entry<String, Ref> r = next;
|
||||||
|
next = peek();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry<String, Ref> peek() {
|
||||||
|
if (packedIdx < packed.size() && looseIdx < loose.size()) {
|
||||||
|
Ref p = packed.get(packedIdx);
|
||||||
|
Ref l = loose.get(looseIdx);
|
||||||
|
int cmp = RefComparator.compareTo(p, l);
|
||||||
|
if (cmp < 0) {
|
||||||
|
packedIdx++;
|
||||||
|
return toEntry(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmp == 0)
|
||||||
|
packedIdx++;
|
||||||
|
looseIdx++;
|
||||||
|
return toEntry(resolveLoose(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (looseIdx < loose.size())
|
||||||
|
return toEntry(resolveLoose(loose.get(looseIdx++)));
|
||||||
|
if (packedIdx < packed.size())
|
||||||
|
return toEntry(packed.get(packedIdx++));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ref resolveLoose(final Ref l) {
|
||||||
|
if (resolvedIdx < resolved.size()) {
|
||||||
|
Ref r = resolved.get(resolvedIdx);
|
||||||
|
int cmp = RefComparator.compareTo(l, r);
|
||||||
|
if (cmp == 0) {
|
||||||
|
resolvedIdx++;
|
||||||
|
return r;
|
||||||
|
} else if (cmp > 0) {
|
||||||
|
// WTF, we have a symbolic entry but no match
|
||||||
|
// in the loose collection. That's an error.
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ent toEntry(Ref p) {
|
||||||
|
if (p.getName().startsWith(prefix))
|
||||||
|
return new Ent(p);
|
||||||
|
packedIdx = packed.size();
|
||||||
|
looseIdx = loose.size();
|
||||||
|
resolvedIdx = resolved.size();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Ent implements Entry<String, Ref> {
|
||||||
|
private Ref ref;
|
||||||
|
|
||||||
|
Ent(Ref ref) {
|
||||||
|
this.ref = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return toMapKey(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref getValue() {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ref setValue(Ref value) {
|
||||||
|
Ref prior = put(getKey(), value);
|
||||||
|
ref = value;
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getKey().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof Map.Entry) {
|
||||||
|
final Object key = ((Map.Entry) obj).getKey();
|
||||||
|
final Object val = ((Map.Entry) obj).getValue();
|
||||||
|
if (key instanceof String && val instanceof Ref) {
|
||||||
|
final Ref r = (Ref) val;
|
||||||
|
if (r.getName().equals(ref.getName())) {
|
||||||
|
final ObjectId a = r.getObjectId();
|
||||||
|
final ObjectId b = ref.getObjectId();
|
||||||
|
if (a != null && b != null && AnyObjectId.equals(a, b))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ref.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue