fce44ae9 Andy McCall 1 month ago ↶
1
2
3
use super :: format:: plural;
4
use super :: open;
135212de Andy McCall 1 month ago ↶
5
use super :: ui;
fce44ae9 Andy McCall 1 month ago ↶
6
use anyhow:: { Context, Result } ;
7
use oxy_core:: repo:: Repo;
8
use std:: path:: PathBuf;
9
2863456a Andy McCall 1 month ago ↶
10
pub fn clone ( url : & str , dest : Option< PathBuf> , max_blob : Option< & str > , sparse : & [String], depth : Option< u32 > ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
11
let dest = dest.unwrap_or_else ( | | {
12
let base = url.trim_end_matches ( ' /' ) .rsplit ( ' /' ) .next ( ) .unwrap_or ( " repo" ) ;
13
PathBuf:: from( base)
14
} ) ;
b3c5a59a Andy McCall 1 month ago ↶
15
let max_blob = max_blob.map ( oxy_core:: repo:: parse_size) .transpose ( ) ? ;
135212de Andy McCall 1 month ago ↶
16
let sp = ui:: spinner( & format! ( " Cloning {url} …" ) ) ;
2863456a Andy McCall 1 month ago ↶
17
let repo = match depth {
18
Some ( d) => Repo:: clone_shallow( url, & dest, d, max_blob, sparse) ? ,
19
None => Repo:: clone_with( url, & dest, max_blob, sparse) ? ,
20
} ;
fce44ae9 Andy McCall 1 month ago ↶
21
let heads = repo.bookmark_heads ( ) ? ;
94084087 Andy McCall 1 month ago ↶
22
sp.done ( & format! ( " {} into {} {} " , ui:: done( " Cloned" ) , ui:: path( repo.root ( ) .display ( ) ) , ui:: dim( format! ( " ({url} , {} with {} )" , ui:: bookmark( & repo.config.bookmark) , plural ( heads.len ( ) , " head" ) ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
23
Ok ( 0 )
24
}
25
26
pub fn remote_add ( name : & str , url : & str ) -> Result< i32 > {
27
let mut repo = open ( ) ? ;
28
repo.config.set ( & format! ( " remote.{name} .url" ) , url) ? ;
29
repo.save_config ( ) ? ;
94084087 Andy McCall 1 month ago ↶
30
println! ( " {} remote {} {} {url} " , ui:: done( " Added" ) , ui:: bookmark( name) , ui:: dim( ui:: glyph( " →" , " ->" ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
31
Ok ( 0 )
32
}
33
34
pub fn remote_remove ( name : & str ) -> Result< i32 > {
35
let mut repo = open ( ) ? ;
36
repo.config.set ( & format! ( " remote.{name} .url" ) , " " ) ? ;
37
repo.save_config ( ) ? ;
94084087 Andy McCall 1 month ago ↶
38
println! ( " {} remote {} " , ui:: done( " Removed" ) , ui:: bookmark( name) ) ;
fce44ae9 Andy McCall 1 month ago ↶
39
Ok ( 0 )
40
}
41
42
pub fn remote_list ( ) -> Result< i32 > {
43
let repo = open ( ) ? ;
44
let view = repo.view ( ) ? ;
94084087 Andy McCall 1 month ago ↶
45
if repo.config.remotes.is_empty ( ) {
46
println! ( " {} " , ui:: dim( " No remotes; add one with oxy remote add <name> <url>." ) ) ;
47
}
fce44ae9 Andy McCall 1 month ago ↶
48
for ( name, url) in & repo.config.remotes {
94084087 Andy McCall 1 month ago ↶
49
println! ( " {} {} {} {} " , ui:: ok( ui:: glyph( " ⇄" , " <>" ) ) , ui:: bookmark( name) , ui:: sep( ) , ui:: dim( url) ) ;
fce44ae9 Andy McCall 1 month ago ↶
50
if let Some ( bms) = view.remotes.get ( name) {
51
for ( bm, st) in bms {
52
let heads = repo.store ( ) .get_state ( st) .map ( | s | s.heads.len ( ) ) .unwrap_or ( 0 ) ;
94084087 Andy McCall 1 month ago ↶
53
println! ( " {} {} {} {} " , ui:: dim( ui:: glyph( " ↳" , " ->" ) ) , ui:: id( format! ( " {name} /{bm} " ) ) , ui:: sep( ) , ui:: dim( plural ( heads, " head" ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
54
}
55
}
56
}
57
Ok ( 0 )
58
}
59
60
fn remote_name ( remote : Option< & str > ) -> String {
61
remote.unwrap_or ( " origin" ) .to_string ( )
62
}
63
64
pub fn fetch ( remote : Option< & str > ) -> Result< i32 > {
65
let repo = open ( ) ? ;
66
let remote = remote_name ( remote) ;
135212de Andy McCall 1 month ago ↶
67
let sp = ui:: spinner( & format! ( " Fetching from {remote} …" ) ) ;
83e0677b Andy McCall 1 month ago ↶
68
let report = repo.fetch ( & remote) ? ;
135212de Andy McCall 1 month ago ↶
69
sp.clear ( ) ;
94084087 Andy McCall 1 month ago ↶
70
if report.refs.is_empty ( ) {
71
println! ( " {} {} " , ui:: done( " Fetched" ) , ui:: dim( format! ( " nothing new from {remote} " ) ) ) ;
72
}
83e0677b Andy McCall 1 month ago ↶
73
for ( name, heads, n) in & report.refs {
94084087 Andy McCall 1 month ago ↶
74
let what = if * n = = 0 { ui:: dim( " up to date" ) } else { ui:: ok( plural ( * n, " new change" ) ) } ;
75
println! ( " {} {} {} {} {} {what} " , ui:: done( " Fetched" ) , ui:: id( format! ( " {remote} /{name} " ) ) , ui:: sep( ) , ui:: dim( plural ( heads.len ( ) , " head" ) ) , ui:: sep( ) ) ;
83e0677b Andy McCall 1 month ago ↶
76
}
132b884b Andy McCall 1 month ago ↶
77
if report.new_tags > 0 {
94084087 Andy McCall 1 month ago ↶
78
println! ( " {} {} " , ui:: note( ) , ui:: dim( format! ( " {} adopted" , plural ( report.new_tags, " tag" ) ) ) ) ;
132b884b Andy McCall 1 month ago ↶
79
}
83e0677b Andy McCall 1 month ago ↶
80
if report.unverified {
94084087 Andy McCall 1 month ago ↶
81
eprintln! ( " {} {remote} has no trust policy; nothing fetched from it was verified" , ui:: caution( ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
82
}
83
Ok ( 0 )
84
}
85
86
pub fn pull ( remote : Option< & str > ) -> Result< i32 > {
87
let repo = open ( ) ? ;
88
let remote = remote_name ( remote) ;
135212de Andy McCall 1 month ago ↶
89
let sp = ui:: spinner( & format! ( " Pulling from {remote} …" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
90
let r = repo.pull ( & remote) ? ;
91
if ! r.changed {
94084087 Andy McCall 1 month ago ↶
92
sp.done ( & format! ( " {} {} " , ui:: ok( ui:: glyph( " ✔" , " *" ) ) , ui:: dim( " Already up to date." ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
93
return Ok ( 0 ) ;
94
}
135212de Andy McCall 1 month ago ↶
95
sp.done ( & format! (
96
" {} {} now has {} {} " ,
94084087 Andy McCall 1 month ago ↶
97
ui:: done( " Pulled:" ) ,
98
ui:: bookmark( & repo.config.bookmark) ,
fce44ae9 Andy McCall 1 month ago ↶
99
plural ( r.heads.len ( ) , " head" ) ,
94084087 Andy McCall 1 month ago ↶
100
ui:: dim( if r.checked_out { " ; working copy updated" } else { " ; run oxy checkout to update the working copy" } )
135212de Andy McCall 1 month ago ↶
101
) ) ;
fce44ae9 Andy McCall 1 month ago ↶
102
let snap = repo.snapshot ( " oxy pull" ) ? ;
103
if snap.base_snapshot.is_conflicted ( ) {
94084087 Andy McCall 1 month ago ↶
104
println! ( " {} {} " , ui:: caution( ) , ui:: warn( " the result has conflicts; see oxy status" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
105
}
106
Ok ( 0 )
107
}
108
7b9809e6 Andy McCall 1 month ago ↶
109
pub fn push ( remote : Option< & str > , force : bool , bookmark : Option< & str > ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
110
let repo = open ( ) ? ;
111
let remote = remote_name ( remote) ;
135212de Andy McCall 1 month ago ↶
112
let sp = ui:: spinner( & format! ( " Pushing to {remote} …" ) ) ;
7b9809e6 Andy McCall 1 month ago ↶
113
let results = match bookmark {
114
Some ( b) => repo.push_bookmark ( & remote, b, force) ? ,
115
None => repo.push ( & remote, force) ? ,
116
} ;
135212de Andy McCall 1 month ago ↶
117
sp.clear ( ) ;
fce44ae9 Andy McCall 1 month ago ↶
118
let mut code = 0 ;
135212de Andy McCall 1 month ago ↶
119
for r in results {
fce44ae9 Andy McCall 1 month ago ↶
120
if r.ok {
94084087 Andy McCall 1 month ago ↶
121
println! ( " {} {} {} {} " , ui:: done( " Pushed" ) , ui:: id( format! ( " {remote} /{} " , r.name) ) , ui:: sep( ) , ui:: dim( if r.reason.is_empty ( ) { " updated" } else { r.reason.as_str ( ) } ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
122
} else {
94084087 Andy McCall 1 month ago ↶
123
println! ( " {} {} {} {} {} " , ui:: err( ui:: glyph( " ✖" , " x" ) ) , ui:: err( " Rejected" ) , ui:: id( format! ( " {remote} /{} " , r.name) ) , ui:: sep( ) , r.reason) ;
fce44ae9 Andy McCall 1 month ago ↶
124
code = 1 ;
125
}
126
}
127
Ok ( code)
128
}
129
2b5ee371 Andy McCall 1 month ago ↶
130
pub fn serve ( root : PathBuf, bind : & str , tokens : Vec< String> , tls : Option< ( PathBuf, PathBuf) > ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
131
let listener = std:: net:: TcpListener:: bind( bind) .with_context ( | | format! ( " binding {bind} " ) ) ? ;
132
let root = root.canonicalize ( ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
133
let tls = tls.map ( | ( cert , key ) | oxy_server:: TlsFiles { cert, key } ) ;
fce44ae9 Andy McCall 1 month ago ↶
134
println! (
2b5ee371 Andy McCall 1 month ago ↶
135
" Serving repositories under {} on {} ://{bind} /<repo>/oxy/v1/ ({} )" ,
fce44ae9 Andy McCall 1 month ago ↶
136
root.display ( ) ,
2b5ee371 Andy McCall 1 month ago ↶
137
if tls.is_some ( ) { " https" } else { " http" } ,
fce44ae9 Andy McCall 1 month ago ↶
138
if tokens.is_empty ( ) { " open: no push tokens configured" } else { " push requires a token" }
139
) ;
2b5ee371 Andy McCall 1 month ago ↶
140
if tls.is_none ( ) & & ! bind.starts_with ( " 127." ) & & ! bind.starts_with ( " localhost" ) & & ! bind.starts_with ( " [::1]" ) {
141
eprintln! ( " warning: serving plain HTTP on a non-loopback address; put a TLS terminator in front or pass --tls-cert/--tls-key" ) ;
142
}
143
oxy_server:: run_blocking( listener, oxy_server:: ServerConfig { root, tokens, tls } ) ? ;
fce44ae9 Andy McCall 1 month ago ↶
144
Ok ( 0 )
145
}
79ab8528 Andy McCall 1 month ago ↶
146
30ea9c1e Andy McCall 1 month ago ↶
147
pub fn git_export ( git_dir : & std:: path:: Path, branch : Option< & str > ) -> Result< i32 > {
79ab8528 Andy McCall 1 month ago ↶
148
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
149
let sp = ui:: spinner( " Exporting to Git…" ) ;
30ea9c1e Andy McCall 1 month ago ↶
150
let r = match branch {
151
Some ( b) => repo.git_export_as ( git_dir, b) ? ,
152
None => repo.git_export ( git_dir) ? ,
153
} ;
135212de Andy McCall 1 month ago ↶
154
sp.clear ( ) ;
79ab8528 Andy McCall 1 month ago ↶
155
println! (
94084087 Andy McCall 1 month ago ↶
156
" {} {} to {} as branch {} {} " ,
157
ui:: done( " Exported" ) ,
e8b4e156 Andy McCall 1 month ago ↶
158
plural ( r.exported, " new state" ) ,
94084087 Andy McCall 1 month ago ↶
159
ui:: path( git_dir.display ( ) ) ,
160
ui:: bookmark( & r.branch) ,
161
ui:: dim( format! ( " (currently {} ; {} already exported)" , plural ( r.heads, " head" ) , r.skipped) )
79ab8528 Andy McCall 1 month ago ↶
162
) ;
163
Ok ( 0 )
164
}
c3069537 Andy McCall 1 month ago ↶
165
30ea9c1e Andy McCall 1 month ago ↶
166
pub fn git_fetch ( url : & str , branch : & str ) -> Result< i32 > {
167
let repo = open ( ) ? ;
168
let sp = ui:: spinner( & format! ( " Fetching {url} {branch} …" ) ) ;
169
let r = repo.git_fetch ( url, branch) ? ;
170
sp.clear ( ) ;
171
if r.commits = = 0 {
172
println! ( " {} {} " , ui:: done( " Fetched" ) , ui:: dim( format! ( " nothing new from {url} {branch} " ) ) ) ;
173
} else {
174
println! (
175
" {} {} ({} merges) from {} {} {} {} now has {} " ,
176
ui:: done( " Fetched" ) ,
177
plural ( r.commits, " new commit" ) ,
178
r.merges,
179
ui:: path( url) ,
180
ui:: bookmark( branch) ,
181
ui:: sep( ) ,
182
ui:: bookmark( & repo.config.bookmark) ,
183
plural ( r.heads, " head" )
184
) ;
185
if ! repo.snapshot ( " oxy git fetch" ) ? .changed {
186
println! ( " {} {} " , ui:: note( ) , ui:: dim( " working copy updated" ) ) ;
187
} else {
188
println! ( " {} {} " , ui:: note( ) , ui:: dim( " run oxy checkout to update the working copy" ) ) ;
189
}
190
}
191
if r.skipped_submodules > 0 {
192
println! ( " {} {} " , ui:: note( ) , ui:: dim( format! ( " skipped {} submodule entries" , r.skipped_submodules) ) ) ;
193
}
194
Ok ( 0 )
195
}
196
197
pub fn git_push ( url : & str , branch : & str ) -> Result< i32 > {
198
let repo = open ( ) ? ;
199
let sp = ui:: spinner( & format! ( " Pushing to {url} {branch} …" ) ) ;
200
let r = repo.git_push ( url, branch) ? ;
201
sp.clear ( ) ;
202
println! (
203
" {} {} to {} {} {} " ,
204
ui:: done( " Pushed" ) ,
205
plural ( r.exported, " new commit" ) ,
206
ui:: path( url) ,
207
ui:: bookmark( & r.branch) ,
208
ui:: dim( format! ( " ({} already there)" , r.skipped) )
209
) ;
210
Ok ( 0 )
211
}
212
c3069537 Andy McCall 1 month ago ↶
213
pub fn git_import ( git_dir : & std:: path:: Path, dest : & std:: path:: Path, branch : & str ) -> Result< i32 > {
214
let repo = Repo:: init( dest) ? ;
215
let started = std:: time:: Instant:: now( ) ;
135212de Andy McCall 1 month ago ↶
216
let sp = ui:: spinner( & format! ( " Importing {} {branch} …" , git_dir.display ( ) ) ) ;
c3069537 Andy McCall 1 month ago ↶
217
let r = repo.import_git ( git_dir, branch) ? ;
135212de Andy McCall 1 month ago ↶
218
sp.clear ( ) ;
c3069537 Andy McCall 1 month ago ↶
219
println! (
94084087 Andy McCall 1 month ago ↶
220
" {} {} ({} merges) from {} {} into {} {} " ,
221
ui:: done( " Imported" ) ,
c3069537 Andy McCall 1 month ago ↶
222
plural ( r.commits, " commit" ) ,
223
r.merges,
94084087 Andy McCall 1 month ago ↶
224
ui:: path( git_dir.display ( ) ) ,
225
ui:: bookmark( branch) ,
226
ui:: path( repo.root ( ) .display ( ) ) ,
227
ui:: dim( format! ( " in {:.1?} ; {} now has {} " , started.elapsed ( ) , repo.config.bookmark, plural ( r.heads, " head" ) ) )
c3069537 Andy McCall 1 month ago ↶
228
) ;
94084087 Andy McCall 1 month ago ↶
229
if r.skipped_submodules > 0 {
230
println! ( " {} {} " , ui:: note( ) , ui:: dim( format! ( " skipped {} submodule entries" , r.skipped_submodules) ) ) ;
231
}
c3069537 Andy McCall 1 month ago ↶
232
Ok ( 0 )
233
}
2863456a Andy McCall 1 month ago ↶
234
235
pub fn deepen ( remote : & str , depth : u32 ) -> Result< i32 > {
236
let repo = open ( ) ? ;
237
if ! repo.is_shallow ( ) {
238
println! ( " {} " , ui:: dim( " This repository is not shallow; nothing to deepen." ) ) ;
239
return Ok ( 0 ) ;
240
}
241
let sp = ui:: spinner( & format! ( " Fetching {depth} more levels from {remote} …" ) ) ;
242
let n = repo.deepen ( remote, depth) ? ;
243
sp.done ( & format! ( " {} {} ; {} " , ui:: ok( " Deepened by" ) , plural ( n, " change" ) , if repo.is_shallow ( ) { " still shallow" .to_string ( ) } else { ui:: ok( " history is now complete" ) } ) ) ;
244
Ok ( 0 )
245
}