Mercurial > jhg
diff src/org/tmatesoft/hg/internal/WorkingDirFileWriter.java @ 580:bd5926e24aa3
Respect unix flags for checkout/revert
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 19 Apr 2013 20:30:34 +0200 |
parents | becd2a1310a2 |
children | 6526d8adbc0f |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/WorkingDirFileWriter.java Wed Apr 17 16:06:10 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/WorkingDirFileWriter.java Fri Apr 19 20:30:34 2013 +0200 @@ -16,6 +16,8 @@ */ package org.tmatesoft.hg.internal; +import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -23,6 +25,7 @@ import java.nio.channels.FileChannel; import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.util.ByteChannel; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.LogFacility.Severity; @@ -37,29 +40,65 @@ private final Internals hgRepo; + private final boolean execCap, symlinkCap; + private final FileSystemHelper fileFlagsHelper; private File dest; private FileChannel destChannel; private int totalBytesWritten; + private ByteArrayChannel linkChannel; + private int fmode; public WorkingDirFileWriter(Internals internalRepo) { hgRepo = internalRepo; + execCap = Internals.checkSupportsExecutables(internalRepo.getRepo().getWorkingDir()); + symlinkCap = Internals.checkSupportsSymlinks(internalRepo.getRepo().getWorkingDir()); + if (symlinkCap || execCap) { + fileFlagsHelper = new FileSystemHelper(internalRepo.getSessionContext()); + } else { + fileFlagsHelper = null; + } } /** - * Information purposes only, to find out trouble location if {@link #processFile(HgDataFile, int)} fails + * Writes content of specified file revision into local filesystem, or create a symlink according to flags. + * Executable bit is set if specified and filesystem supports it. */ - public File getDestinationFile() { - return dest; - } - - public void processFile(HgDataFile df, int fileRevIndex) throws IOException { + public void processFile(HgDataFile df, int fileRevIndex, HgManifest.Flags flags) throws IOException { try { prepare(df.getPath()); + if (flags != HgManifest.Flags.Link) { + destChannel = new FileOutputStream(dest).getChannel(); + } else { + linkChannel = new ByteArrayChannel(); + } df.contentWithFilters(fileRevIndex, this); } catch (CancelledException ex) { hgRepo.getSessionContext().getLog().dump(getClass(), Severity.Error, ex, "Our impl doesn't throw cancellation"); + } finally { + if (flags != HgManifest.Flags.Link) { + destChannel.close(); + destChannel = null; + // leave dest in case anyone enquires with #getDestinationFile + } } - finish(); + if (linkChannel != null && symlinkCap) { + assert flags == HgManifest.Flags.Link; + fileFlagsHelper.createSymlink(dest.getParentFile(), dest.getName(), linkChannel.toArray()); + } else if (flags == HgManifest.Flags.Exec && execCap) { + fileFlagsHelper.setExecutableBit(dest.getParentFile(), dest.getName()); + } + // Although HgWCStatusCollector treats 644 (`hg manifest -v`) and 664 (my fs) the same, it's better + // to detect actual flags here + fmode = flags.fsMode(); // default to one from manifest + if (fileFlagsHelper != null) { + // if neither execBit nor link is supported by fs, it's unlikely file mode is supported, too. + try { + fmode = fileFlagsHelper.getFileMode(dest, fmode); + } catch (IOException ex) { + // Warn, we've got default value and can live with it + hgRepo.getSessionContext().getLog().dump(getClass(), Warn, ex, "Failed get file access rights"); + } + } } public void prepare(Path fname) throws IOException { @@ -68,22 +107,39 @@ if (fpath.indexOf('/') != -1) { dest.getParentFile().mkdirs(); } - destChannel = new FileOutputStream(dest).getChannel(); + destChannel = null; + linkChannel = null; totalBytesWritten = 0; + fmode = 0; } public int write(ByteBuffer buffer) throws IOException, CancelledException { - int written = destChannel.write(buffer); + final int written; + if (linkChannel != null) { + written = linkChannel.write(buffer); + } else { + written = destChannel.write(buffer); + } totalBytesWritten += written; return written; } - public void finish() throws IOException { - destChannel.close(); - dest = null; + /** + * Information purposes only, to find out trouble location if {@link #processFile(HgDataFile, int)} fails + */ + public File getDestinationFile() { + return dest; } public int bytesWritten() { return totalBytesWritten; } + + public int fmode() { + return fmode; + } + + public int mtime() { + return (int) (dest.lastModified() / 1000); + } }