1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.lib;
44
45 import static java.lang.Long.valueOf;
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertTrue;
49
50 import java.io.File;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.util.TreeSet;
54
55 import org.eclipse.jgit.api.Git;
56 import org.eclipse.jgit.junit.RepositoryTestCase;
57 import org.eclipse.jgit.treewalk.FileTreeIterator;
58 import org.eclipse.jgit.treewalk.FileTreeIteratorWithTimeControl;
59 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
60 import org.eclipse.jgit.util.FileUtils;
61 import org.junit.Test;
62
63 public class RacyGitTests extends RepositoryTestCase {
64 @Test
65 public void testIterator()
66 throws IllegalStateException, IOException, InterruptedException {
67 TreeSet<Long> modTimes = new TreeSet<>();
68 File lastFile = null;
69 for (int i = 0; i < 10; i++) {
70 lastFile = new File(db.getWorkTree(), "0." + i);
71 FileUtils.createNewFile(lastFile);
72 if (i == 5)
73 fsTick(lastFile);
74 }
75 modTimes.add(valueOf(fsTick(lastFile)));
76 for (int i = 0; i < 10; i++) {
77 lastFile = new File(db.getWorkTree(), "1." + i);
78 FileUtils.createNewFile(lastFile);
79 }
80 modTimes.add(valueOf(fsTick(lastFile)));
81 for (int i = 0; i < 10; i++) {
82 lastFile = new File(db.getWorkTree(), "2." + i);
83 FileUtils.createNewFile(lastFile);
84 if (i % 4 == 0)
85 fsTick(lastFile);
86 }
87 FileTreeIteratorWithTimeControl fileIt = new FileTreeIteratorWithTimeControl(
88 db, modTimes);
89 try (NameConflictTreeWalk tw = new NameConflictTreeWalk(db)) {
90 tw.addTree(fileIt);
91 tw.setRecursive(true);
92 FileTreeIterator t;
93 long t0 = 0;
94 for (int i = 0; i < 10; i++) {
95 assertTrue(tw.next());
96 t = tw.getTree(0, FileTreeIterator.class);
97 if (i == 0) {
98 t0 = t.getEntryLastModified();
99 } else {
100 assertEquals(t0, t.getEntryLastModified());
101 }
102 }
103 long t1 = 0;
104 for (int i = 0; i < 10; i++) {
105 assertTrue(tw.next());
106 t = tw.getTree(0, FileTreeIterator.class);
107 if (i == 0) {
108 t1 = t.getEntryLastModified();
109 assertTrue(t1 > t0);
110 } else {
111 assertEquals(t1, t.getEntryLastModified());
112 }
113 }
114 long t2 = 0;
115 for (int i = 0; i < 10; i++) {
116 assertTrue(tw.next());
117 t = tw.getTree(0, FileTreeIterator.class);
118 if (i == 0) {
119 t2 = t.getEntryLastModified();
120 assertTrue(t2 > t1);
121 } else {
122 assertEquals(t2, t.getEntryLastModified());
123 }
124 }
125 }
126 }
127
128 @Test
129 public void testRacyGitDetection() throws Exception {
130
131 try (Git git = new Git(db)) {
132 git.reset().call();
133 }
134
135
136
137 fsTick(db.getIndexFile());
138
139
140 File a = addToWorkDir("a", "a");
141 File b = addToWorkDir("b", "b");
142 assertTrue(a.setLastModified(b.lastModified()));
143 assertTrue(b.setLastModified(b.lastModified()));
144
145
146
147 fsTick(b);
148
149
150 resetIndex(new FileTreeIterator(db));
151
152 assertEquals(
153 "[a, mode:100644, time:t0, length:1, content:a]"
154 + "[b, mode:100644, time:t0, length:1, content:b]",
155 indexState(SMUDGE | MOD_TIME | LENGTH | CONTENT));
156
157
158 fsTick(db.getIndexFile());
159
160
161
162
163 File updatedA = addToWorkDir("a", "a2");
164 assertTrue(updatedA.setLastModified(updatedA.lastModified() + 100));
165 resetIndex(new FileTreeIterator(db));
166 assertTrue(db.getIndexFile()
167 .setLastModified(updatedA.lastModified() + 90));
168
169 db.readDirCache();
170
171 assertEquals(
172 "[a, mode:100644, time:t1, smudged, length:0, content:a2]"
173 + "[b, mode:100644, time:t0, length:1, content:b]",
174 indexState(SMUDGE | MOD_TIME | LENGTH | CONTENT));
175 }
176
177 private File addToWorkDir(String path, String content) throws IOException {
178 File f = new File(db.getWorkTree(), path);
179 try (FileOutputStream fos = new FileOutputStream(f)) {
180 fos.write(content.getBytes(UTF_8));
181 return f;
182 }
183 }
184 }