@@ -67,7 +67,10 @@ fn ident(location: &Location) -> String {
67
67
let last = path. components ( ) . last ( ) . unwrap ( ) ;
68
68
str:: from_utf8 ( last) . unwrap ( ) . to_str ( )
69
69
}
70
- Remote ( ref url) => url. path . as_slice ( ) . split ( '/' ) . last ( ) . unwrap ( ) . to_str ( )
70
+ Remote ( ref url) => {
71
+ let path = canonicalize_url ( url. path . as_slice ( ) ) ;
72
+ path. as_slice ( ) . split ( '/' ) . last ( ) . unwrap ( ) . to_str ( )
73
+ }
71
74
} ;
72
75
73
76
let ident = if ident. as_slice ( ) == "" {
@@ -76,7 +79,47 @@ fn ident(location: &Location) -> String {
76
79
ident
77
80
} ;
78
81
79
- format ! ( "{}-{}" , ident, to_hex( hasher. hash( & location. to_str( ) ) ) )
82
+ let location = canonicalize_url ( location. to_str ( ) . as_slice ( ) ) ;
83
+
84
+ format ! ( "{}-{}" , ident, to_hex( hasher. hash( & location. as_slice( ) ) ) )
85
+ }
86
+
87
+ fn strip_trailing_slash < ' a > ( path : & ' a str ) -> & ' a str {
88
+ // Remove the trailing '/' so that 'split' doesn't give us
89
+ // an empty string, making '../foo/' and '../foo' both
90
+ // result in the name 'foo' (#84)
91
+ if path. as_bytes ( ) . last ( ) != Some ( & ( '/' as u8 ) ) {
92
+ path. clone ( )
93
+ } else {
94
+ path. slice ( 0 , path. len ( ) - 1 )
95
+ }
96
+ }
97
+
98
+ // Some hacks and heuristics for making equivalent URLs hash the same
99
+ fn canonicalize_url ( url : & str ) -> String {
100
+ let url = strip_trailing_slash ( url) ;
101
+
102
+ // HACKHACK: For github URL's specifically just lowercase
103
+ // everything. GitHub traits both the same, but they hash
104
+ // differently, and we're gonna be hashing them. This wants a more
105
+ // general solution, and also we're almost certainly not using the
106
+ // same case conversion rules that GitHub does. (#84)
107
+
108
+ let lower_url = url. chars ( ) . map ( |c|c. to_lowercase ( ) ) . collect :: < String > ( ) ;
109
+ let url = if lower_url. as_slice ( ) . contains ( "github.com" ) {
110
+ lower_url
111
+ } else {
112
+ url. to_string ( )
113
+ } ;
114
+
115
+ // Repos generally can be accessed with or w/o '.git'
116
+ let url = if !url. as_slice ( ) . ends_with ( ".git" ) {
117
+ url
118
+ } else {
119
+ url. as_slice ( ) . slice ( 0 , url. len ( ) - 4 ) . to_string ( )
120
+ } ;
121
+
122
+ return url;
80
123
}
81
124
82
125
impl < ' a , ' b > Show for GitSource < ' a , ' b > {
@@ -150,6 +193,26 @@ mod test {
150
193
assert_eq ! ( ident. as_slice( ) , "_empty-fc065c9b6b16fc00" ) ;
151
194
}
152
195
196
+ #[ test]
197
+ fn test_canonicalize_idents_by_stripping_trailing_url_slash ( ) {
198
+ let ident1 = ident ( & Remote ( url ( "https://github.com/PistonDevelopers/piston/" ) ) ) ;
199
+ let ident2 = ident ( & Remote ( url ( "https://github.com/PistonDevelopers/piston" ) ) ) ;
200
+ assert_eq ! ( ident1, ident2) ;
201
+ }
202
+
203
+ #[ test]
204
+ fn test_canonicalize_idents_by_lowercasing_github_urls ( ) {
205
+ let ident1 = ident ( & Remote ( url ( "https://github.com/PistonDevelopers/piston" ) ) ) ;
206
+ let ident2 = ident ( & Remote ( url ( "https://github.com/pistondevelopers/piston" ) ) ) ;
207
+ assert_eq ! ( ident1, ident2) ;
208
+ }
209
+
210
+ #[ test]
211
+ fn test_canonicalize_idents_by_stripping_dot_git ( ) {
212
+ let ident1 = ident ( & Remote ( url ( "https://github.com/PistonDevelopers/piston" ) ) ) ;
213
+ let ident2 = ident ( & Remote ( url ( "https://github.com/PistonDevelopers/piston.git" ) ) ) ;
214
+ assert_eq ! ( ident1, ident2) ;
215
+ }
153
216
154
217
fn url ( s : & str ) -> Url {
155
218
url:: from_str ( s) . unwrap ( )
0 commit comments