1
1
use bstr:: BStr ;
2
+ use filetime:: { set_file_mtime, FileTime } ;
3
+ use gix_index as index;
2
4
use gix_worktree:: fs:: Capabilities ;
3
5
use gix_worktree:: index:: status:: content:: FastEq ;
4
6
use gix_worktree:: index:: status:: worktree:: { self , Options } ;
5
7
use gix_worktree:: index:: status:: { Change , Recorder } ;
6
8
7
9
use crate :: fixture_path;
8
10
11
+ // since tests are fixtures a bunch of stat information (like inode number)
12
+ // changes when extracting the data so we need to disable all advanced stat
13
+ // changes and only look at mtime seconds and file size to properly
14
+ // test all code paths (and to trigger racy git)
15
+ const TEST_OPTIONS : index:: entry:: stat:: Options = index:: entry:: stat:: Options {
16
+ trust_ctime : false ,
17
+ check_stat : false ,
18
+ use_nsec : false ,
19
+ use_stdev : false ,
20
+ } ;
21
+
9
22
fn fixture ( name : & str , expected_status : & [ ( & BStr , Option < Change > , bool ) ] ) {
10
23
let worktree = fixture_path ( name) ;
11
24
let git_dir = worktree. join ( ".git" ) ;
@@ -18,6 +31,7 @@ fn fixture(name: &str, expected_status: &[(&BStr, Option<Change>, bool)]) {
18
31
& FastEq ,
19
32
Options {
20
33
fs : Capabilities :: probe ( git_dir) ,
34
+ stat : TEST_OPTIONS ,
21
35
..Options :: default ( )
22
36
} ,
23
37
)
@@ -43,6 +57,7 @@ fn removed() {
43
57
fn unchanged ( ) {
44
58
fixture ( "status_unchanged" , & [ ] ) ;
45
59
}
60
+
46
61
#[ test]
47
62
fn modified ( ) {
48
63
// run the same status check twice to ensure that racy detection
@@ -108,3 +123,69 @@ fn modified() {
108
123
] ,
109
124
) ;
110
125
}
126
+
127
+ #[ test]
128
+ fn racy_git ( ) {
129
+ let timestamp = 940040400 ;
130
+ // we need a writable fixture because we have to mess with mtimes manually,
131
+ // because touch -d respects the locale so the test wouldn't work depending
132
+ // on the timesystem you run your test in
133
+ let dir = gix_testtools:: scripted_fixture_writable ( "racy_git.sh" ) . expect ( "script works" ) ;
134
+ let worktree = dir. path ( ) ;
135
+ let git_dir = worktree. join ( ".git" ) ;
136
+ let fs = Capabilities :: probe ( & git_dir) ;
137
+ let mut index = gix_index:: File :: at ( git_dir. join ( "index" ) , gix_hash:: Kind :: Sha1 , Default :: default ( ) ) . unwrap ( ) ;
138
+ // we artificially mess with mtime so that it's before the timestamp
139
+ // saved by git. This would usually mean an invalid fs/invalid index file
140
+ // and as a result the racy git mitigation doesn't work and the worktree
141
+ // shows up as unchanged even tough the file did change. This case
142
+ // doesn't happen in the realworld (except for file corruption) but
143
+ // makes sure we are actually hitting the right codepath
144
+ index. entries [ 0 ] . stat . mtime . secs = timestamp;
145
+ set_file_mtime ( worktree. join ( "content" ) , FileTime :: from_unix_time ( timestamp as i64 , 0 ) )
146
+ . expect ( "changing filetime works" ) ;
147
+ let mut recorder = Recorder :: default ( ) ;
148
+ worktree:: changes_to_obtain (
149
+ & mut index,
150
+ & worktree,
151
+ & mut recorder,
152
+ & FastEq ,
153
+ Options {
154
+ fs,
155
+ stat : TEST_OPTIONS ,
156
+ ..Options :: default ( )
157
+ } ,
158
+ )
159
+ . unwrap ( ) ;
160
+ assert_eq ! ( recorder. records, & [ ] ) ;
161
+
162
+ // now we also backdate the index timestamp to match the artificially created
163
+ // mtime above this is now a realistic realworld racecondition which
164
+ // should trigger racy git and cause proper output
165
+ index. set_timestamp ( FileTime :: from_unix_time ( timestamp as i64 , 0 ) ) ;
166
+ let mut recorder = Recorder :: default ( ) ;
167
+ worktree:: changes_to_obtain (
168
+ & mut index,
169
+ & worktree,
170
+ & mut recorder,
171
+ & FastEq ,
172
+ Options {
173
+ fs,
174
+ stat : TEST_OPTIONS ,
175
+ ..Options :: default ( )
176
+ } ,
177
+ )
178
+ . unwrap ( ) ;
179
+ // be handled correctly by the racy git detection
180
+ assert_eq ! (
181
+ recorder. records,
182
+ & [ (
183
+ BStr :: new( b"content" ) ,
184
+ Some ( Change :: Modification {
185
+ executable_bit_changed: false ,
186
+ content_change: Some ( ( ) ) ,
187
+ } ) ,
188
+ false
189
+ ) ]
190
+ ) ;
191
+ }
0 commit comments