comparison 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
comparison
equal deleted inserted replaced
579:36e36b926747 580:bd5926e24aa3
14 * the terms of a license other than GNU General Public License 14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com 15 * contact TMate Software at support@hg4j.com
16 */ 16 */
17 package org.tmatesoft.hg.internal; 17 package org.tmatesoft.hg.internal;
18 18
19 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn;
20
19 import java.io.File; 21 import java.io.File;
20 import java.io.FileOutputStream; 22 import java.io.FileOutputStream;
21 import java.io.IOException; 23 import java.io.IOException;
22 import java.nio.ByteBuffer; 24 import java.nio.ByteBuffer;
23 import java.nio.channels.FileChannel; 25 import java.nio.channels.FileChannel;
24 26
25 import org.tmatesoft.hg.repo.HgDataFile; 27 import org.tmatesoft.hg.repo.HgDataFile;
28 import org.tmatesoft.hg.repo.HgManifest;
26 import org.tmatesoft.hg.util.ByteChannel; 29 import org.tmatesoft.hg.util.ByteChannel;
27 import org.tmatesoft.hg.util.CancelledException; 30 import org.tmatesoft.hg.util.CancelledException;
28 import org.tmatesoft.hg.util.LogFacility.Severity; 31 import org.tmatesoft.hg.util.LogFacility.Severity;
29 import org.tmatesoft.hg.util.Path; 32 import org.tmatesoft.hg.util.Path;
30 33
35 */ 38 */
36 public class WorkingDirFileWriter implements ByteChannel { 39 public class WorkingDirFileWriter implements ByteChannel {
37 40
38 41
39 private final Internals hgRepo; 42 private final Internals hgRepo;
43 private final boolean execCap, symlinkCap;
44 private final FileSystemHelper fileFlagsHelper;
40 private File dest; 45 private File dest;
41 private FileChannel destChannel; 46 private FileChannel destChannel;
42 private int totalBytesWritten; 47 private int totalBytesWritten;
48 private ByteArrayChannel linkChannel;
49 private int fmode;
43 50
44 public WorkingDirFileWriter(Internals internalRepo) { 51 public WorkingDirFileWriter(Internals internalRepo) {
45 hgRepo = internalRepo; 52 hgRepo = internalRepo;
53 execCap = Internals.checkSupportsExecutables(internalRepo.getRepo().getWorkingDir());
54 symlinkCap = Internals.checkSupportsSymlinks(internalRepo.getRepo().getWorkingDir());
55 if (symlinkCap || execCap) {
56 fileFlagsHelper = new FileSystemHelper(internalRepo.getSessionContext());
57 } else {
58 fileFlagsHelper = null;
59 }
46 } 60 }
47 61
48 /** 62 /**
49 * Information purposes only, to find out trouble location if {@link #processFile(HgDataFile, int)} fails 63 * Writes content of specified file revision into local filesystem, or create a symlink according to flags.
64 * Executable bit is set if specified and filesystem supports it.
50 */ 65 */
51 public File getDestinationFile() { 66 public void processFile(HgDataFile df, int fileRevIndex, HgManifest.Flags flags) throws IOException {
52 return dest;
53 }
54
55 public void processFile(HgDataFile df, int fileRevIndex) throws IOException {
56 try { 67 try {
57 prepare(df.getPath()); 68 prepare(df.getPath());
69 if (flags != HgManifest.Flags.Link) {
70 destChannel = new FileOutputStream(dest).getChannel();
71 } else {
72 linkChannel = new ByteArrayChannel();
73 }
58 df.contentWithFilters(fileRevIndex, this); 74 df.contentWithFilters(fileRevIndex, this);
59 } catch (CancelledException ex) { 75 } catch (CancelledException ex) {
60 hgRepo.getSessionContext().getLog().dump(getClass(), Severity.Error, ex, "Our impl doesn't throw cancellation"); 76 hgRepo.getSessionContext().getLog().dump(getClass(), Severity.Error, ex, "Our impl doesn't throw cancellation");
77 } finally {
78 if (flags != HgManifest.Flags.Link) {
79 destChannel.close();
80 destChannel = null;
81 // leave dest in case anyone enquires with #getDestinationFile
82 }
61 } 83 }
62 finish(); 84 if (linkChannel != null && symlinkCap) {
85 assert flags == HgManifest.Flags.Link;
86 fileFlagsHelper.createSymlink(dest.getParentFile(), dest.getName(), linkChannel.toArray());
87 } else if (flags == HgManifest.Flags.Exec && execCap) {
88 fileFlagsHelper.setExecutableBit(dest.getParentFile(), dest.getName());
89 }
90 // Although HgWCStatusCollector treats 644 (`hg manifest -v`) and 664 (my fs) the same, it's better
91 // to detect actual flags here
92 fmode = flags.fsMode(); // default to one from manifest
93 if (fileFlagsHelper != null) {
94 // if neither execBit nor link is supported by fs, it's unlikely file mode is supported, too.
95 try {
96 fmode = fileFlagsHelper.getFileMode(dest, fmode);
97 } catch (IOException ex) {
98 // Warn, we've got default value and can live with it
99 hgRepo.getSessionContext().getLog().dump(getClass(), Warn, ex, "Failed get file access rights");
100 }
101 }
63 } 102 }
64 103
65 public void prepare(Path fname) throws IOException { 104 public void prepare(Path fname) throws IOException {
66 String fpath = fname.toString(); 105 String fpath = fname.toString();
67 dest = new File(hgRepo.getRepo().getWorkingDir(), fpath); 106 dest = new File(hgRepo.getRepo().getWorkingDir(), fpath);
68 if (fpath.indexOf('/') != -1) { 107 if (fpath.indexOf('/') != -1) {
69 dest.getParentFile().mkdirs(); 108 dest.getParentFile().mkdirs();
70 } 109 }
71 destChannel = new FileOutputStream(dest).getChannel(); 110 destChannel = null;
111 linkChannel = null;
72 totalBytesWritten = 0; 112 totalBytesWritten = 0;
113 fmode = 0;
73 } 114 }
74 115
75 public int write(ByteBuffer buffer) throws IOException, CancelledException { 116 public int write(ByteBuffer buffer) throws IOException, CancelledException {
76 int written = destChannel.write(buffer); 117 final int written;
118 if (linkChannel != null) {
119 written = linkChannel.write(buffer);
120 } else {
121 written = destChannel.write(buffer);
122 }
77 totalBytesWritten += written; 123 totalBytesWritten += written;
78 return written; 124 return written;
79 } 125 }
80 126
81 public void finish() throws IOException { 127 /**
82 destChannel.close(); 128 * Information purposes only, to find out trouble location if {@link #processFile(HgDataFile, int)} fails
83 dest = null; 129 */
130 public File getDestinationFile() {
131 return dest;
84 } 132 }
85 133
86 public int bytesWritten() { 134 public int bytesWritten() {
87 return totalBytesWritten; 135 return totalBytesWritten;
88 } 136 }
137
138 public int fmode() {
139 return fmode;
140 }
141
142 public int mtime() {
143 return (int) (dest.lastModified() / 1000);
144 }
89 } 145 }