1 /*
2  * libgit2 "fetch" example - shows how to fetch remote data
3  *
4  * Written by the libgit2 contributors
5  *
6  * To the extent possible under law, the author(s) have dedicated all copyright
7  * and related and neighboring rights to this software to the public domain
8  * worldwide. This software is distributed without any warranty.
9  *
10  * You should have received a copy of the CC0 Public Domain Dedication along
11  * with this software. If not, see
12  * <http://creativecommons.org/publicdomain/zero/1.0/>.
13  */
14 
15 #![deny(warnings)]
16 
17 use docopt::Docopt;
18 use git2::{AutotagOption, FetchOptions, RemoteCallbacks, Repository};
19 use serde_derive::Deserialize;
20 use std::io::{self, Write};
21 use std::str;
22 
23 #[derive(Deserialize)]
24 struct Args {
25     arg_remote: Option<String>,
26 }
27 
run(args: &Args) -> Result<(), git2::Error>28 fn run(args: &Args) -> Result<(), git2::Error> {
29     let repo = Repository::open(".")?;
30     let remote = args.arg_remote.as_ref().map(|s| &s[..]).unwrap_or("origin");
31 
32     // Figure out whether it's a named remote or a URL
33     println!("Fetching {} for repo", remote);
34     let mut cb = RemoteCallbacks::new();
35     let mut remote = repo
36         .find_remote(remote)
37         .or_else(|_| repo.remote_anonymous(remote))?;
38     cb.sideband_progress(|data| {
39         print!("remote: {}", str::from_utf8(data).unwrap());
40         io::stdout().flush().unwrap();
41         true
42     });
43 
44     // This callback gets called for each remote-tracking branch that gets
45     // updated. The message we output depends on whether it's a new one or an
46     // update.
47     cb.update_tips(|refname, a, b| {
48         if a.is_zero() {
49             println!("[new]     {:20} {}", b, refname);
50         } else {
51             println!("[updated] {:10}..{:10} {}", a, b, refname);
52         }
53         true
54     });
55 
56     // Here we show processed and total objects in the pack and the amount of
57     // received data. Most frontends will probably want to show a percentage and
58     // the download rate.
59     cb.transfer_progress(|stats| {
60         if stats.received_objects() == stats.total_objects() {
61             print!(
62                 "Resolving deltas {}/{}\r",
63                 stats.indexed_deltas(),
64                 stats.total_deltas()
65             );
66         } else if stats.total_objects() > 0 {
67             print!(
68                 "Received {}/{} objects ({}) in {} bytes\r",
69                 stats.received_objects(),
70                 stats.total_objects(),
71                 stats.indexed_objects(),
72                 stats.received_bytes()
73             );
74         }
75         io::stdout().flush().unwrap();
76         true
77     });
78 
79     // Download the packfile and index it. This function updates the amount of
80     // received data and the indexer stats which lets you inform the user about
81     // progress.
82     let mut fo = FetchOptions::new();
83     fo.remote_callbacks(cb);
84     remote.download(&[], Some(&mut fo))?;
85 
86     {
87         // If there are local objects (we got a thin pack), then tell the user
88         // how many objects we saved from having to cross the network.
89         let stats = remote.stats();
90         if stats.local_objects() > 0 {
91             println!(
92                 "\rReceived {}/{} objects in {} bytes (used {} local \
93                  objects)",
94                 stats.indexed_objects(),
95                 stats.total_objects(),
96                 stats.received_bytes(),
97                 stats.local_objects()
98             );
99         } else {
100             println!(
101                 "\rReceived {}/{} objects in {} bytes",
102                 stats.indexed_objects(),
103                 stats.total_objects(),
104                 stats.received_bytes()
105             );
106         }
107     }
108 
109     // Disconnect the underlying connection to prevent from idling.
110     remote.disconnect();
111 
112     // Update the references in the remote's namespace to point to the right
113     // commits. This may be needed even if there was no packfile to download,
114     // which can happen e.g. when the branches have been changed but all the
115     // needed objects are available locally.
116     remote.update_tips(None, true, AutotagOption::Unspecified, None)?;
117 
118     Ok(())
119 }
120 
main()121 fn main() {
122     const USAGE: &str = "
123 usage: fetch [options] [<remote>]
124 
125 Options:
126     -h, --help          show this message
127 ";
128 
129     let args = Docopt::new(USAGE)
130         .and_then(|d| d.deserialize())
131         .unwrap_or_else(|e| e.exit());
132     match run(&args) {
133         Ok(()) => {}
134         Err(e) => println!("error: {}", e),
135     }
136 }
137