1
2
3
4
5
6
7
8
9
10
11
12 package org.eclipse.jgit.internal.storage.file;
13
14 import java.io.File;
15 import java.io.IOException;
16 import java.nio.file.AtomicMoveNotSupportedException;
17 import java.nio.file.StandardCopyOption;
18
19 import org.eclipse.jgit.lib.Constants;
20 import org.eclipse.jgit.lib.ObjectId;
21 import org.eclipse.jgit.lib.RefRename;
22 import org.eclipse.jgit.lib.RefUpdate;
23 import org.eclipse.jgit.lib.RefUpdate.Result;
24 import org.eclipse.jgit.revwalk.RevWalk;
25 import org.eclipse.jgit.util.FileUtils;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29
30
31
32
33
34
35
36
37
38
39 class RefDirectoryRename extends RefRename {
40 private static final Logger LOG = LoggerFactory
41 .getLogger(RefDirectoryRename.class);
42
43 private final RefDirectory refdb;
44
45
46
47
48
49
50
51
52 private ObjectId objId;
53
54
55 private RefDirectoryUpdate tmp;
56
57 RefDirectoryRename(RefDirectoryUpdate src, RefDirectoryUpdate dst) {
58 super(src, dst);
59 refdb = src.getRefDatabase();
60 }
61
62
63 @Override
64 protected Result doRename() throws IOException {
65 if (source.getRef().isSymbolic())
66 return Result.IO_FAILURE;
67
68 objId = source.getOldObjectId();
69 boolean updateHEAD = needToUpdateHEAD();
70 tmp = refdb.newTemporaryUpdate();
71 try (RevWalk rw = new RevWalk(refdb.getRepository())) {
72
73 tmp.setNewObjectId(objId);
74 tmp.setForceUpdate(true);
75 tmp.disableRefLog();
76 switch (tmp.update(rw)) {
77 case NEW:
78 case FORCED:
79 case NO_CHANGE:
80 break;
81 default:
82 return tmp.getResult();
83 }
84
85
86
87 if (!renameLog(source, tmp))
88 return Result.IO_FAILURE;
89
90
91
92
93 RefUpdate dst = destination;
94 if (updateHEAD) {
95 if (!linkHEAD(destination)) {
96 renameLog(tmp, source);
97 return Result.LOCK_FAILURE;
98 }
99
100
101 dst = refdb.newUpdate(Constants.HEAD, false);
102 dst.setRefLogIdent(destination.getRefLogIdent());
103 dst.setRefLogMessage(destination.getRefLogMessage(), false);
104 }
105
106
107 source.setExpectedOldObjectId(objId);
108 source.setForceUpdate(true);
109 source.disableRefLog();
110 if (source.delete(rw) != Result.FORCED) {
111 renameLog(tmp, source);
112 if (updateHEAD)
113 linkHEAD(source);
114 return source.getResult();
115 }
116
117
118 if (!renameLog(tmp, destination)) {
119 renameLog(tmp, source);
120 source.setExpectedOldObjectId(ObjectId.zeroId());
121 source.setNewObjectId(objId);
122 source.update(rw);
123 if (updateHEAD)
124 linkHEAD(source);
125 return Result.IO_FAILURE;
126 }
127
128
129 dst.setExpectedOldObjectId(ObjectId.zeroId());
130 dst.setNewObjectId(objId);
131 if (dst.update(rw) != Result.NEW) {
132
133
134 if (renameLog(destination, tmp))
135 renameLog(tmp, source);
136 source.setExpectedOldObjectId(ObjectId.zeroId());
137 source.setNewObjectId(objId);
138 source.update(rw);
139 if (updateHEAD)
140 linkHEAD(source);
141 return dst.getResult();
142 }
143
144 return Result.RENAMED;
145 } finally {
146
147 try {
148 refdb.delete(tmp);
149 } catch (IOException err) {
150 FileUtils.delete(refdb.fileFor(tmp.getName()));
151 }
152 }
153 }
154
155 private boolean renameLog(RefUpdate src, RefUpdate dst) {
156 File srcLog = refdb.logFor(src.getName());
157 File dstLog = refdb.logFor(dst.getName());
158
159 if (!srcLog.exists())
160 return true;
161
162 if (!rename(srcLog, dstLog))
163 return false;
164
165 try {
166 final int levels = RefDirectory.levelsIn(src.getName()) - 2;
167 RefDirectory.delete(srcLog, levels);
168 return true;
169 } catch (IOException e) {
170 rename(dstLog, srcLog);
171 return false;
172 }
173 }
174
175 private static boolean rename(File src, File dst) {
176 try {
177 FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
178 return true;
179 } catch (AtomicMoveNotSupportedException e) {
180 LOG.error(e.getMessage(), e);
181 } catch (IOException e) {
182
183 }
184
185 File dir = dst.getParentFile();
186 if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory())
187 return false;
188 try {
189 FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
190 return true;
191 } catch (IOException e) {
192 LOG.error(e.getMessage(), e);
193 return false;
194 }
195 }
196
197 private boolean linkHEAD(RefUpdate target) {
198 try {
199 RefUpdate u = refdb.newUpdate(Constants.HEAD, false);
200 u.disableRefLog();
201 switch (u.link(target.getName())) {
202 case NEW:
203 case FORCED:
204 case NO_CHANGE:
205 return true;
206 default:
207 return false;
208 }
209 } catch (IOException e) {
210 return false;
211 }
212 }
213 }