Mercurial > jhg
comparison src/org/tmatesoft/hg/internal/WorkingDirFileWriter.java @ 705:b4242b7e7dfe
Merge command: implement conflict resolution alternatives
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Thu, 15 Aug 2013 18:43:50 +0200 |
| parents | 6526d8adbc0f |
| children | 42b88709e41d |
comparison
equal
deleted
inserted
replaced
| 704:7743a9c10bfa | 705:b4242b7e7dfe |
|---|---|
| 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; | 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; |
| 20 | 20 |
| 21 import java.io.File; | 21 import java.io.File; |
| 22 import java.io.FileOutputStream; | 22 import java.io.FileOutputStream; |
| 23 import java.io.IOException; | 23 import java.io.IOException; |
| 24 import java.io.InputStream; | |
| 24 import java.nio.ByteBuffer; | 25 import java.nio.ByteBuffer; |
| 25 import java.nio.channels.FileChannel; | 26 import java.nio.channels.FileChannel; |
| 26 | 27 |
| 28 import org.tmatesoft.hg.core.HgFileRevision; | |
| 29 import org.tmatesoft.hg.core.HgIOException; | |
| 27 import org.tmatesoft.hg.repo.HgDataFile; | 30 import org.tmatesoft.hg.repo.HgDataFile; |
| 28 import org.tmatesoft.hg.repo.HgManifest; | 31 import org.tmatesoft.hg.repo.HgManifest; |
| 29 import org.tmatesoft.hg.repo.HgRuntimeException; | 32 import org.tmatesoft.hg.repo.HgRuntimeException; |
| 30 import org.tmatesoft.hg.util.ByteChannel; | 33 import org.tmatesoft.hg.util.ByteChannel; |
| 31 import org.tmatesoft.hg.util.CancelledException; | 34 import org.tmatesoft.hg.util.CancelledException; |
| 63 /** | 66 /** |
| 64 * Writes content of specified file revision into local filesystem, or create a symlink according to flags. | 67 * Writes content of specified file revision into local filesystem, or create a symlink according to flags. |
| 65 * Executable bit is set if specified and filesystem supports it. | 68 * Executable bit is set if specified and filesystem supports it. |
| 66 * @throws HgRuntimeException | 69 * @throws HgRuntimeException |
| 67 */ | 70 */ |
| 68 public void processFile(HgDataFile df, int fileRevIndex, HgManifest.Flags flags) throws IOException, HgRuntimeException { | 71 public void processFile(final HgDataFile df, final int fileRevIndex, HgManifest.Flags flags) throws HgIOException, HgRuntimeException { |
| 72 processFile(df.getPath(), new Fetch() { | |
| 73 public void readInto(ByteChannel ch) { | |
| 74 try { | |
| 75 df.contentWithFilters(fileRevIndex, ch); | |
| 76 } catch (CancelledException ex) { | |
| 77 handleUnexpectedCancel(ex); | |
| 78 } | |
| 79 } | |
| 80 }, flags); | |
| 81 } | |
| 82 | |
| 83 public void processFile(final HgFileRevision fr) throws HgIOException, HgRuntimeException { | |
| 84 processFile(fr.getPath(), new Fetch() { | |
| 85 | |
| 86 public void readInto(ByteChannel ch) throws IOException, HgRuntimeException { | |
| 87 try { | |
| 88 fr.putContentTo(ch); | |
| 89 } catch (CancelledException ex) { | |
| 90 handleUnexpectedCancel(ex); | |
| 91 } | |
| 92 } | |
| 93 }, fr.getFileFlags()); | |
| 94 } | |
| 95 | |
| 96 /** | |
| 97 * Closes supplied content stream | |
| 98 */ | |
| 99 public void processFile(Path fname, final InputStream content, HgManifest.Flags flags) throws HgIOException, HgRuntimeException { | |
| 100 processFile(fname, new Fetch() { | |
| 101 | |
| 102 public void readInto(ByteChannel ch) throws IOException, HgRuntimeException { | |
| 103 try { | |
| 104 ByteBuffer bb = ByteBuffer.wrap(new byte[8*1024]); | |
| 105 int r; | |
| 106 while ((r = content.read(bb.array())) != -1) { | |
| 107 bb.position(0).limit(r); | |
| 108 for (int wrote = 0; wrote < r; ) { | |
| 109 r -= wrote; | |
| 110 wrote = ch.write(bb); | |
| 111 assert bb.remaining() == r - wrote; | |
| 112 } | |
| 113 } | |
| 114 } catch (CancelledException ex) { | |
| 115 handleUnexpectedCancel(ex); | |
| 116 } | |
| 117 } | |
| 118 }, flags); | |
| 119 } | |
| 120 | |
| 121 private interface Fetch { | |
| 122 void readInto(ByteChannel ch) throws IOException, HgRuntimeException; | |
| 123 } | |
| 124 | |
| 125 private void processFile(Path fname, Fetch fetch, HgManifest.Flags flags) throws HgIOException, HgRuntimeException { | |
| 69 try { | 126 try { |
| 70 prepare(df.getPath()); | 127 byte[] symlinkContent = null; |
| 71 if (flags != HgManifest.Flags.Link) { | |
| 72 destChannel = new FileOutputStream(dest).getChannel(); | |
| 73 } else { | |
| 74 linkChannel = new ByteArrayChannel(); | |
| 75 } | |
| 76 df.contentWithFilters(fileRevIndex, this); | |
| 77 } catch (CancelledException ex) { | |
| 78 hgRepo.getSessionContext().getLog().dump(getClass(), Severity.Error, ex, "Our impl doesn't throw cancellation"); | |
| 79 } finally { | |
| 80 if (flags != HgManifest.Flags.Link) { | |
| 81 destChannel.close(); | |
| 82 destChannel = null; | |
| 83 // leave dest in case anyone enquires with #getDestinationFile | |
| 84 } | |
| 85 } | |
| 86 if (linkChannel != null && symlinkCap) { | |
| 87 assert flags == HgManifest.Flags.Link; | |
| 88 fileFlagsHelper.createSymlink(dest.getParentFile(), dest.getName(), linkChannel.toArray()); | |
| 89 } else if (flags == HgManifest.Flags.Exec && execCap) { | |
| 90 fileFlagsHelper.setExecutableBit(dest.getParentFile(), dest.getName()); | |
| 91 } | |
| 92 // Although HgWCStatusCollector treats 644 (`hg manifest -v`) and 664 (my fs) the same, it's better | |
| 93 // to detect actual flags here | |
| 94 fmode = flags.fsMode(); // default to one from manifest | |
| 95 if (fileFlagsHelper != null) { | |
| 96 // if neither execBit nor link is supported by fs, it's unlikely file mode is supported, too. | |
| 97 try { | 128 try { |
| 98 fmode = fileFlagsHelper.getFileMode(dest, fmode); | 129 prepare(fname, flags); |
| 99 } catch (IOException ex) { | 130 fetch.readInto(this); |
| 100 // Warn, we've got default value and can live with it | 131 } finally { |
| 101 hgRepo.getSessionContext().getLog().dump(getClass(), Warn, ex, "Failed get file access rights"); | 132 symlinkContent = close(fname, flags); |
| 102 } | 133 } |
| 103 } | 134 if (flags == HgManifest.Flags.Link && symlinkCap) { |
| 104 } | 135 assert symlinkContent != null; |
| 105 | 136 fileFlagsHelper.createSymlink(dest.getParentFile(), dest.getName(), symlinkContent); |
| 106 public void prepare(Path fname) throws IOException { | 137 } else if (flags == HgManifest.Flags.Exec && execCap) { |
| 138 fileFlagsHelper.setExecutableBit(dest.getParentFile(), dest.getName()); | |
| 139 } | |
| 140 // Although HgWCStatusCollector treats 644 (`hg manifest -v`) and 664 (my fs) the same, it's better | |
| 141 // to detect actual flags here | |
| 142 fmode = flags.fsMode(); // default to one from manifest | |
| 143 if (fileFlagsHelper != null) { | |
| 144 // if neither execBit nor link is supported by fs, it's unlikely file mode is supported, too. | |
| 145 try { | |
| 146 fmode = fileFlagsHelper.getFileMode(dest, fmode); | |
| 147 } catch (IOException ex) { | |
| 148 // Warn, we've got default value and can live with it | |
| 149 hgRepo.getSessionContext().getLog().dump(getClass(), Warn, ex, "Failed get file access rights"); | |
| 150 } | |
| 151 } | |
| 152 } catch (IOException ex) { | |
| 153 String msg = String.format("Failed to write file %s to the working directory", fname); | |
| 154 throw new HgIOException(msg, ex, dest); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 private void prepare(Path fname, HgManifest.Flags flags) throws IOException { | |
| 107 String fpath = fname.toString(); | 159 String fpath = fname.toString(); |
| 108 dest = new File(hgRepo.getRepo().getWorkingDir(), fpath); | 160 dest = new File(hgRepo.getRepo().getWorkingDir(), fpath); |
| 109 if (fpath.indexOf('/') != -1) { | 161 if (fpath.indexOf('/') != -1) { |
| 110 dest.getParentFile().mkdirs(); | 162 dest.getParentFile().mkdirs(); |
| 111 } | 163 } |
| 112 destChannel = null; | 164 destChannel = null; |
| 113 linkChannel = null; | 165 linkChannel = null; |
| 114 totalBytesWritten = 0; | 166 totalBytesWritten = 0; |
| 115 fmode = 0; | 167 fmode = 0; |
| 168 if (flags != HgManifest.Flags.Link) { | |
| 169 destChannel = new FileOutputStream(dest).getChannel(); | |
| 170 } else { | |
| 171 linkChannel = new ByteArrayChannel(); | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 private byte[] close(Path fname, HgManifest.Flags flags) throws IOException { | |
| 176 if (flags != HgManifest.Flags.Link) { | |
| 177 destChannel.close(); | |
| 178 destChannel = null; | |
| 179 // leave dest in case anyone enquires with #getDestinationFile | |
| 180 } | |
| 181 if (linkChannel != null) { | |
| 182 final byte[] rv = linkChannel.toArray(); | |
| 183 linkChannel = null; | |
| 184 return rv; | |
| 185 } | |
| 186 return null; | |
| 116 } | 187 } |
| 117 | 188 |
| 118 public int write(ByteBuffer buffer) throws IOException, CancelledException { | 189 public int write(ByteBuffer buffer) throws IOException, CancelledException { |
| 119 final int written; | 190 final int written; |
| 120 if (linkChannel != null) { | 191 if (linkChannel != null) { |
| 142 } | 213 } |
| 143 | 214 |
| 144 public int mtime() { | 215 public int mtime() { |
| 145 return (int) (dest.lastModified() / 1000); | 216 return (int) (dest.lastModified() / 1000); |
| 146 } | 217 } |
| 218 | |
| 219 private void handleUnexpectedCancel(CancelledException ex) { | |
| 220 hgRepo.getSessionContext().getLog().dump(WorkingDirFileWriter.class, Severity.Error, ex, "Our impl doesn't throw cancellation"); | |
| 221 } | |
| 147 } | 222 } |
