Mercurial > jhg
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 } |