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 }