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
7421309f Andy McCall 1 month ago ↶
10
pub fn clone ( url : & str , dest : Option< PathBuf> , max_blob : Option< & str > , sparse : & [String]) -> 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} …" ) ) ;
7421309f Andy McCall 1 month ago ↶
17
let repo = Repo:: clone_with( url, & dest, max_blob, sparse) ? ;
fce44ae9 Andy McCall 1 month ago ↶
18
let heads = repo.bookmark_heads ( ) ? ;
135212de Andy McCall 1 month ago ↶
19
sp.done ( & format! ( " {} into {} ({} on {} : {} )" , ui:: ok( " Cloned" ) , ui:: path( repo.root ( ) .display ( ) ) , url, ui:: id( & repo.config.bookmark) , plural ( heads.len ( ) , " head" ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
20
Ok ( 0 )
21
}
22
23
pub fn remote_add ( name : & str , url : & str ) -> Result< i32 > {
24
let mut repo = open ( ) ? ;
25
repo.config.set ( & format! ( " remote.{name} .url" ) , url) ? ;
26
repo.save_config ( ) ? ;
27
println! ( " Added remote {name} = {url} " ) ;
28
Ok ( 0 )
29
}
30
31
pub fn remote_remove ( name : & str ) -> Result< i32 > {
32
let mut repo = open ( ) ? ;
33
repo.config.set ( & format! ( " remote.{name} .url" ) , " " ) ? ;
34
repo.save_config ( ) ? ;
35
println! ( " Removed remote {name} " ) ;
36
Ok ( 0 )
37
}
38
39
pub fn remote_list ( ) -> Result< i32 > {
40
let repo = open ( ) ? ;
41
let view = repo.view ( ) ? ;
42
for ( name, url) in & repo.config.remotes {
43
println! ( " {name} \t {url} " ) ;
44
if let Some ( bms) = view.remotes.get ( name) {
45
for ( bm, st) in bms {
46
let heads = repo.store ( ) .get_state ( st) .map ( | s | s.heads.len ( ) ) .unwrap_or ( 0 ) ;
47
println! ( " {bm} : {} " , plural ( heads, " head" ) ) ;
48
}
49
}
50
}
51
Ok ( 0 )
52
}
53
54
fn remote_name ( remote : Option< & str > ) -> String {
55
remote.unwrap_or ( " origin" ) .to_string ( )
56
}
57
58
pub fn fetch ( remote : Option< & str > ) -> Result< i32 > {
59
let repo = open ( ) ? ;
60
let remote = remote_name ( remote) ;
135212de Andy McCall 1 month ago ↶
61
let sp = ui:: spinner( & format! ( " Fetching from {remote} …" ) ) ;
83e0677b Andy McCall 1 month ago ↶
62
let report = repo.fetch ( & remote) ? ;
135212de Andy McCall 1 month ago ↶
63
sp.clear ( ) ;
83e0677b Andy McCall 1 month ago ↶
64
for ( name, heads, n) in & report.refs {
135212de Andy McCall 1 month ago ↶
65
println! ( " {} {} , {} " , ui:: id( format! ( " {remote} /{name} " ) ) , plural ( heads.len ( ) , " head" ) , plural ( * n, " new change" ) ) ;
83e0677b Andy McCall 1 month ago ↶
66
}
67
if report.unverified {
135212de Andy McCall 1 month ago ↶
68
eprintln! ( " {} {remote} has no trust policy; nothing fetched from it was verified" , ui:: warn( " warning:" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
69
}
70
Ok ( 0 )
71
}
72
73
pub fn pull ( remote : Option< & str > ) -> Result< i32 > {
74
let repo = open ( ) ? ;
75
let remote = remote_name ( remote) ;
135212de Andy McCall 1 month ago ↶
76
let sp = ui:: spinner( & format! ( " Pulling from {remote} …" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
77
let r = repo.pull ( & remote) ? ;
78
if ! r.changed {
135212de Andy McCall 1 month ago ↶
79
sp.done ( & ui:: dim( " Already up to date." ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
80
return Ok ( 0 ) ;
81
}
135212de Andy McCall 1 month ago ↶
82
sp.done ( & format! (
83
" {} {} now has {} {} " ,
84
ui:: ok( " Pulled:" ) ,
85
ui:: id( & repo.config.bookmark) ,
fce44ae9 Andy McCall 1 month ago ↶
86
plural ( r.heads.len ( ) , " head" ) ,
87
if r.checked_out { " ; working copy updated" } else { " ; run `oxy checkout` to update the working copy" }
135212de Andy McCall 1 month ago ↶
88
) ) ;
fce44ae9 Andy McCall 1 month ago ↶
89
let snap = repo.snapshot ( " oxy pull" ) ? ;
90
if snap.base_snapshot.is_conflicted ( ) {
135212de Andy McCall 1 month ago ↶
91
println! ( " {} " , ui:: warn( " the result has conflicts; see oxy status" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
92
}
93
Ok ( 0 )
94
}
95
96
pub fn push ( remote : Option< & str > , force : bool ) -> Result< i32 > {
97
let repo = open ( ) ? ;
98
let remote = remote_name ( remote) ;
135212de Andy McCall 1 month ago ↶
99
let sp = ui:: spinner( & format! ( " Pushing to {remote} …" ) ) ;
100
let results = repo.push ( & remote, force) ? ;
101
sp.clear ( ) ;
fce44ae9 Andy McCall 1 month ago ↶
102
let mut code = 0 ;
135212de Andy McCall 1 month ago ↶
103
for r in results {
fce44ae9 Andy McCall 1 month ago ↶
104
if r.ok {
135212de Andy McCall 1 month ago ↶
105
println! ( " {} {} " , ui:: id( format! ( " {remote} /{} " , r.name) ) , ui:: ok( if r.reason.is_empty ( ) { " updated" } else { r.reason.as_str ( ) } ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
106
} else {
135212de Andy McCall 1 month ago ↶
107
println! ( " {} {} {} " , ui:: id( format! ( " {remote} /{} " , r.name) ) , ui:: err( " rejected:" ) , r.reason) ;
fce44ae9 Andy McCall 1 month ago ↶
108
code = 1 ;
109
}
110
}
111
Ok ( code)
112
}
113
2b5ee371 Andy McCall 1 month ago ↶
114
pub fn serve ( root : PathBuf, bind : & str , tokens : Vec< String> , tls : Option< ( PathBuf, PathBuf) > ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
115
let listener = std:: net:: TcpListener:: bind( bind) .with_context ( | | format! ( " binding {bind} " ) ) ? ;
116
let root = root.canonicalize ( ) ? ;
2b5ee371 Andy McCall 1 month ago ↶
117
let tls = tls.map ( | ( cert , key ) | oxy_server:: TlsFiles { cert, key } ) ;
fce44ae9 Andy McCall 1 month ago ↶
118
println! (
2b5ee371 Andy McCall 1 month ago ↶
119
" Serving repositories under {} on {} ://{bind} /<repo>/oxy/v1/ ({} )" ,
fce44ae9 Andy McCall 1 month ago ↶
120
root.display ( ) ,
2b5ee371 Andy McCall 1 month ago ↶
121
if tls.is_some ( ) { " https" } else { " http" } ,
fce44ae9 Andy McCall 1 month ago ↶
122
if tokens.is_empty ( ) { " open: no push tokens configured" } else { " push requires a token" }
123
) ;
2b5ee371 Andy McCall 1 month ago ↶
124
if tls.is_none ( ) & & ! bind.starts_with ( " 127." ) & & ! bind.starts_with ( " localhost" ) & & ! bind.starts_with ( " [::1]" ) {
125
eprintln! ( " warning: serving plain HTTP on a non-loopback address; put a TLS terminator in front or pass --tls-cert/--tls-key" ) ;
126
}
127
oxy_server:: run_blocking( listener, oxy_server:: ServerConfig { root, tokens, tls } ) ? ;
fce44ae9 Andy McCall 1 month ago ↶
128
Ok ( 0 )
129
}
79ab8528 Andy McCall 1 month ago ↶
130
131
pub fn git_export ( git_dir : & std:: path:: Path) -> Result< i32 > {
132
let repo = open ( ) ? ;
135212de Andy McCall 1 month ago ↶
133
let sp = ui:: spinner( " Exporting to Git…" ) ;
79ab8528 Andy McCall 1 month ago ↶
134
let r = repo.git_export ( git_dir) ? ;
135212de Andy McCall 1 month ago ↶
135
sp.clear ( ) ;
79ab8528 Andy McCall 1 month ago ↶
136
println! (
137
" Exported {} to {} (branch {} : {} {} ); {} already exported." ,
138
plural ( r.exported, " change" ) ,
139
git_dir.display ( ) ,
140
r.branch,
141
plural ( r.heads, " head" ) ,
142
if r.heads > 1 { " , joined by a synthetic merge commit" } else { " " } ,
143
r.skipped
144
) ;
145
Ok ( 0 )
146
}
c3069537 Andy McCall 1 month ago ↶
147
148
pub fn git_import ( git_dir : & std:: path:: Path, dest : & std:: path:: Path, branch : & str ) -> Result< i32 > {
149
let repo = Repo:: init( dest) ? ;
150
let started = std:: time:: Instant:: now( ) ;
135212de Andy McCall 1 month ago ↶
151
let sp = ui:: spinner( & format! ( " Importing {} {branch} …" , git_dir.display ( ) ) ) ;
c3069537 Andy McCall 1 month ago ↶
152
let r = repo.import_git ( git_dir, branch) ? ;
135212de Andy McCall 1 month ago ↶
153
sp.clear ( ) ;
c3069537 Andy McCall 1 month ago ↶
154
println! (
155
" Imported {} ({} merges) from {} {branch} into {} in {:.1?} : {} now has {} .{} " ,
156
plural ( r.commits, " commit" ) ,
157
r.merges,
158
git_dir.display ( ) ,
159
repo.root ( ) .display ( ) ,
160
started.elapsed ( ) ,
161
repo.config.bookmark,
162
plural ( r.heads, " head" ) ,
163
if r.skipped_submodules > 0 { format! ( " Skipped {} submodule entries." , r.skipped_submodules) } else { String :: new( ) }
164
) ;
165
Ok ( 0 )
166
}