|
|
@ -18,10 +18,13 @@ import static java.util.stream.Collectors.toList; |
|
|
|
import java.io.File; |
|
|
|
import java.io.File; |
|
|
|
import java.io.FileInputStream; |
|
|
|
import java.io.FileInputStream; |
|
|
|
import java.io.FileNotFoundException; |
|
|
|
import java.io.FileNotFoundException; |
|
|
|
|
|
|
|
import java.io.FileOutputStream; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
|
|
|
|
import java.io.OutputStream; |
|
|
|
import java.text.MessageFormat; |
|
|
|
import java.text.MessageFormat; |
|
|
|
import java.text.ParseException; |
|
|
|
import java.text.ParseException; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
|
|
|
|
import java.util.Collections; |
|
|
|
import java.util.HashSet; |
|
|
|
import java.util.HashSet; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Locale; |
|
|
|
import java.util.Locale; |
|
|
@ -50,6 +53,7 @@ import org.eclipse.jgit.lib.ProgressMonitor; |
|
|
|
import org.eclipse.jgit.lib.Ref; |
|
|
|
import org.eclipse.jgit.lib.Ref; |
|
|
|
import org.eclipse.jgit.lib.RefDatabase; |
|
|
|
import org.eclipse.jgit.lib.RefDatabase; |
|
|
|
import org.eclipse.jgit.lib.RefUpdate; |
|
|
|
import org.eclipse.jgit.lib.RefUpdate; |
|
|
|
|
|
|
|
import org.eclipse.jgit.lib.ReflogEntry; |
|
|
|
import org.eclipse.jgit.lib.ReflogReader; |
|
|
|
import org.eclipse.jgit.lib.ReflogReader; |
|
|
|
import org.eclipse.jgit.lib.Repository; |
|
|
|
import org.eclipse.jgit.lib.Repository; |
|
|
|
import org.eclipse.jgit.lib.StoredConfig; |
|
|
|
import org.eclipse.jgit.lib.StoredConfig; |
|
|
@ -173,20 +177,17 @@ public class FileRepository extends Repository { |
|
|
|
|
|
|
|
|
|
|
|
String reftype = repoConfig.getString( |
|
|
|
String reftype = repoConfig.getString( |
|
|
|
ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, |
|
|
|
ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, |
|
|
|
ConfigConstants.CONFIG_KEY_REFSTORAGE); |
|
|
|
ConfigConstants.CONFIG_KEY_REF_STORAGE); |
|
|
|
if (repositoryFormatVersion >= 1 && reftype != null) { |
|
|
|
if (repositoryFormatVersion >= 1 && reftype != null) { |
|
|
|
if (StringUtils.equalsIgnoreCase(reftype, |
|
|
|
if (StringUtils.equalsIgnoreCase(reftype, |
|
|
|
ConfigConstants.CONFIG_REFSTORAGE_REFTABLE)) { |
|
|
|
ConfigConstants.CONFIG_REF_STORAGE_REFTABLE)) { |
|
|
|
refs = new FileReftableDatabase(this, |
|
|
|
refs = new FileReftableDatabase(this); |
|
|
|
new File(getDirectory(), "refs")); //$NON-NLS-1$
|
|
|
|
|
|
|
|
} else if (StringUtils.equalsIgnoreCase(reftype, |
|
|
|
} else if (StringUtils.equalsIgnoreCase(reftype, |
|
|
|
ConfigConstants.CONFIG_REFSTORAGE_REFTREE)) { |
|
|
|
ConfigConstants.CONFIG_REFSTORAGE_REFTREE)) { |
|
|
|
refs = new RefTreeDatabase(this, new RefDirectory(this)); |
|
|
|
refs = new RefTreeDatabase(this, new RefDirectory(this)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
throw new IOException(JGitText.get().unknownRepositoryFormat); |
|
|
|
throw new IOException(JGitText.get().unknownRepositoryFormat); |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (FileReftableDatabase.isReftable(getDirectory())) { |
|
|
|
|
|
|
|
refs = new FileReftableDatabase(this, new File(getDirectory(), "refs")); //$NON-NLS-1$
|
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
refs = new RefDirectory(this); |
|
|
|
refs = new RefDirectory(this); |
|
|
|
} |
|
|
|
} |
|
|
@ -610,15 +611,18 @@ public class FileRepository extends Repository { |
|
|
|
* Converts the RefDatabase from reftable to RefDirectory. This operation is |
|
|
|
* Converts the RefDatabase from reftable to RefDirectory. This operation is |
|
|
|
* not atomic. |
|
|
|
* not atomic. |
|
|
|
* |
|
|
|
* |
|
|
|
|
|
|
|
* @param writeLogs |
|
|
|
|
|
|
|
* whether to write reflogs |
|
|
|
* @param backup |
|
|
|
* @param backup |
|
|
|
* whether to rename or delete the old storage files. If set to |
|
|
|
* whether to rename or delete the old storage files. If set to |
|
|
|
* true, the reftable list is left in "refs.old", and the |
|
|
|
* {@code true}, the reftable list is left in {@code refs.old}, |
|
|
|
* reftable/ dir is left alone. If set to false, the reftable/ |
|
|
|
* and the {@code reftable/} dir is left alone. If set to |
|
|
|
* dir is removed, and "refs" file is removed. |
|
|
|
* {@code false}, the {@code reftable/} dir is removed, and |
|
|
|
|
|
|
|
* {@code refs} file is removed. |
|
|
|
* @throws IOException |
|
|
|
* @throws IOException |
|
|
|
* on IO problem |
|
|
|
* on IO problem |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
void convertToPackedRefs(boolean backup) throws IOException { |
|
|
|
void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException { |
|
|
|
List<Ref> all = refs.getRefs(); |
|
|
|
List<Ref> all = refs.getRefs(); |
|
|
|
File packedRefs = new File(getDirectory(), Constants.PACKED_REFS); |
|
|
|
File packedRefs = new File(getDirectory(), Constants.PACKED_REFS); |
|
|
|
if (packedRefs.exists()) { |
|
|
|
if (packedRefs.exists()) { |
|
|
@ -627,26 +631,26 @@ public class FileRepository extends Repository { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
File refsFile = new File(getDirectory(), "refs"); //$NON-NLS-1$
|
|
|
|
File refsFile = new File(getDirectory(), "refs"); //$NON-NLS-1$
|
|
|
|
|
|
|
|
File refsHeadsFile = new File(refsFile, "heads");//$NON-NLS-1$
|
|
|
|
refs.close(); |
|
|
|
File headFile = new File(getDirectory(), Constants.HEAD); |
|
|
|
|
|
|
|
FileReftableDatabase oldDb = (FileReftableDatabase) refs; |
|
|
|
if (backup) { |
|
|
|
|
|
|
|
File refsOld = new File(getDirectory(), "refs.old"); //$NON-NLS-1$
|
|
|
|
// Remove the dummy files that ensure compatibility with older git
|
|
|
|
if (refsOld.exists()) { |
|
|
|
// versions (see convertToReftable). First make room for refs/heads/
|
|
|
|
throw new IOException(MessageFormat.format( |
|
|
|
refsHeadsFile.delete(); |
|
|
|
JGitText.get().fileAlreadyExists, |
|
|
|
// RefDirectory wants to create the refs/ directory from scratch, so
|
|
|
|
"refs.old")); //$NON-NLS-1$
|
|
|
|
// remove that too.
|
|
|
|
} |
|
|
|
|
|
|
|
FileUtils.rename(refsFile, refsOld); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
refsFile.delete(); |
|
|
|
refsFile.delete(); |
|
|
|
} |
|
|
|
// remove HEAD so its previous invalid value doesn't cause issues.
|
|
|
|
|
|
|
|
headFile.delete(); |
|
|
|
|
|
|
|
|
|
|
|
// This is not atomic, but there is no way to instantiate a RefDirectory
|
|
|
|
// This is not atomic, but there is no way to instantiate a RefDirectory
|
|
|
|
// that is disconnected from the current repo.
|
|
|
|
// that is disconnected from the current repo.
|
|
|
|
refs = new RefDirectory(this); |
|
|
|
RefDirectory refDir = new RefDirectory(this); |
|
|
|
|
|
|
|
refs = refDir; |
|
|
|
refs.create(); |
|
|
|
refs.create(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ReflogWriter logWriter = refDir.newLogWriter(true); |
|
|
|
List<Ref> symrefs = new ArrayList<>(); |
|
|
|
List<Ref> symrefs = new ArrayList<>(); |
|
|
|
BatchRefUpdate bru = refs.newBatchUpdate(); |
|
|
|
BatchRefUpdate bru = refs.newBatchUpdate(); |
|
|
|
for (Ref r : all) { |
|
|
|
for (Ref r : all) { |
|
|
@ -656,6 +660,15 @@ public class FileRepository extends Repository { |
|
|
|
bru.addCommand(new ReceiveCommand(ObjectId.zeroId(), |
|
|
|
bru.addCommand(new ReceiveCommand(ObjectId.zeroId(), |
|
|
|
r.getObjectId(), r.getName())); |
|
|
|
r.getObjectId(), r.getName())); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (writeLogs) { |
|
|
|
|
|
|
|
List<ReflogEntry> logs = oldDb.getReflogReader(r.getName()) |
|
|
|
|
|
|
|
.getReverseEntries(); |
|
|
|
|
|
|
|
Collections.reverse(logs); |
|
|
|
|
|
|
|
for (ReflogEntry e : logs) { |
|
|
|
|
|
|
|
logWriter.log(r.getName(), e); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try (RevWalk rw = new RevWalk(this)) { |
|
|
|
try (RevWalk rw = new RevWalk(this)) { |
|
|
@ -691,24 +704,39 @@ public class FileRepository extends Repository { |
|
|
|
FileUtils.delete(reftableDir, |
|
|
|
FileUtils.delete(reftableDir, |
|
|
|
FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS); |
|
|
|
FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
repoConfig.unset(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, |
|
|
|
repoConfig.unset(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, |
|
|
|
ConfigConstants.CONFIG_KEY_REFSTORAGE); |
|
|
|
ConfigConstants.CONFIG_KEY_REF_STORAGE); |
|
|
|
repoConfig.save(); |
|
|
|
repoConfig.save(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Converts the RefDatabase from RefDirectory to reftable. This operation is |
|
|
|
|
|
|
|
* not atomic. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param writeLogs |
|
|
|
|
|
|
|
* whether to write reflogs |
|
|
|
|
|
|
|
* @param backup |
|
|
|
|
|
|
|
* whether to rename or delete the old storage files. If set to |
|
|
|
|
|
|
|
* {@code true}, the loose refs are left in {@code refs.old}, the |
|
|
|
|
|
|
|
* packed-refs in {@code packed-refs.old} and reflogs in |
|
|
|
|
|
|
|
* {@code refs.old/}. HEAD is left in {@code HEAD.old} and also |
|
|
|
|
|
|
|
* {@code .log} is appended to additional refs. If set to |
|
|
|
|
|
|
|
* {@code false}, the {@code refs/} and {@code logs/} directories |
|
|
|
|
|
|
|
* and {@code HEAD} and additional symbolic refs are removed. |
|
|
|
|
|
|
|
* @throws IOException |
|
|
|
|
|
|
|
* on IO problem |
|
|
|
|
|
|
|
*/ |
|
|
|
@SuppressWarnings("nls") |
|
|
|
@SuppressWarnings("nls") |
|
|
|
void convertToReftable(boolean writeLogs, boolean backup) |
|
|
|
void convertToReftable(boolean writeLogs, boolean backup) |
|
|
|
throws IOException { |
|
|
|
throws IOException { |
|
|
|
File newRefs = new File(getDirectory(), "refs.new"); |
|
|
|
|
|
|
|
File reftableDir = new File(getDirectory(), Constants.REFTABLE); |
|
|
|
File reftableDir = new File(getDirectory(), Constants.REFTABLE); |
|
|
|
|
|
|
|
File headFile = new File(getDirectory(), Constants.HEAD); |
|
|
|
if (reftableDir.exists() && reftableDir.listFiles().length > 0) { |
|
|
|
if (reftableDir.exists() && reftableDir.listFiles().length > 0) { |
|
|
|
throw new IOException(JGitText.get().reftableDirExists); |
|
|
|
throw new IOException(JGitText.get().reftableDirExists); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Ignore return value, as it is tied to temporary newRefs file.
|
|
|
|
// Ignore return value, as it is tied to temporary newRefs file.
|
|
|
|
FileReftableDatabase.convertFrom(this, newRefs, writeLogs); |
|
|
|
FileReftableDatabase.convertFrom(this, writeLogs); |
|
|
|
|
|
|
|
|
|
|
|
File refsFile = new File(getDirectory(), "refs"); |
|
|
|
File refsFile = new File(getDirectory(), "refs"); |
|
|
|
|
|
|
|
|
|
|
@ -716,7 +744,6 @@ public class FileRepository extends Repository { |
|
|
|
File packedRefs = new File(getDirectory(), Constants.PACKED_REFS); |
|
|
|
File packedRefs = new File(getDirectory(), Constants.PACKED_REFS); |
|
|
|
File logsDir = new File(getDirectory(), Constants.LOGS); |
|
|
|
File logsDir = new File(getDirectory(), Constants.LOGS); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<String> additional = getRefDatabase().getAdditionalRefs().stream() |
|
|
|
List<String> additional = getRefDatabase().getAdditionalRefs().stream() |
|
|
|
.map(Ref::getName).collect(toList()); |
|
|
|
.map(Ref::getName).collect(toList()); |
|
|
|
additional.add(Constants.HEAD); |
|
|
|
additional.add(Constants.HEAD); |
|
|
@ -735,7 +762,8 @@ public class FileRepository extends Repository { |
|
|
|
new File(getDirectory(), r + ".old")); |
|
|
|
new File(getDirectory(), r + ".old")); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
packedRefs.delete(); // ignore return value.
|
|
|
|
FileUtils.delete(packedRefs, FileUtils.SKIP_MISSING); |
|
|
|
|
|
|
|
FileUtils.delete(headFile); |
|
|
|
FileUtils.delete(logsDir, FileUtils.RECURSIVE); |
|
|
|
FileUtils.delete(logsDir, FileUtils.RECURSIVE); |
|
|
|
FileUtils.delete(refsFile, FileUtils.RECURSIVE); |
|
|
|
FileUtils.delete(refsFile, FileUtils.RECURSIVE); |
|
|
|
for (String r : additional) { |
|
|
|
for (String r : additional) { |
|
|
@ -743,16 +771,26 @@ public class FileRepository extends Repository { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Put new data.
|
|
|
|
FileUtils.mkdir(refsFile, true); |
|
|
|
FileUtils.rename(newRefs, refsFile); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
refs.close(); |
|
|
|
// By putting in a dummy HEAD, old versions of Git still detect a repo
|
|
|
|
refs = new FileReftableDatabase(this, refsFile); |
|
|
|
// (that they can't read)
|
|
|
|
|
|
|
|
try (OutputStream os = new FileOutputStream(headFile)) { |
|
|
|
|
|
|
|
os.write(Constants.encodeASCII("ref: refs/heads/.invalid")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Some tools might write directly into .git/refs/heads/BRANCH. By
|
|
|
|
|
|
|
|
// putting a file here, this fails spectacularly.
|
|
|
|
|
|
|
|
FileUtils.createNewFile(new File(refsFile, "heads")); |
|
|
|
|
|
|
|
|
|
|
|
repoConfig.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, |
|
|
|
repoConfig.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, |
|
|
|
ConfigConstants.CONFIG_KEY_REFSTORAGE, |
|
|
|
ConfigConstants.CONFIG_KEY_REF_STORAGE, |
|
|
|
ConfigConstants.CONFIG_REFSTORAGE_REFTABLE); |
|
|
|
ConfigConstants.CONFIG_REF_STORAGE_REFTABLE); |
|
|
|
|
|
|
|
repoConfig.setLong(ConfigConstants.CONFIG_CORE_SECTION, null, |
|
|
|
|
|
|
|
ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 1); |
|
|
|
repoConfig.save(); |
|
|
|
repoConfig.save(); |
|
|
|
|
|
|
|
refs.close(); |
|
|
|
|
|
|
|
refs = new FileReftableDatabase(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -775,7 +813,7 @@ public class FileRepository extends Repository { |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (format.equals("refdir")) {//$NON-NLS-1$
|
|
|
|
} else if (format.equals("refdir")) {//$NON-NLS-1$
|
|
|
|
if (refs instanceof FileReftableDatabase) { |
|
|
|
if (refs instanceof FileReftableDatabase) { |
|
|
|
convertToPackedRefs(backup); |
|
|
|
convertToPackedRefs(writeLogs, backup); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
throw new IOException(MessageFormat |
|
|
|
throw new IOException(MessageFormat |
|
|
|