1 /*
2  * libgit2 "init" example - shows how to initialize a new repo
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 git2::{Error, Repository, RepositoryInitMode, RepositoryInitOptions};
18 use std::path::{Path, PathBuf};
19 use structopt::StructOpt;
20 
21 #[derive(StructOpt)]
22 struct Args {
23     #[structopt(name = "directory")]
24     arg_directory: String,
25     #[structopt(name = "quiet", short, long)]
26     /// don't print information to stdout
27     flag_quiet: bool,
28     #[structopt(name = "bare", long)]
29     /// initialize a new bare repository
30     flag_bare: bool,
31     #[structopt(name = "dir", long = "template")]
32     /// use <dir> as an initialization template
33     flag_template: Option<String>,
34     #[structopt(name = "separate-git-dir", long)]
35     /// use <dir> as the .git directory
36     flag_separate_git_dir: Option<String>,
37     #[structopt(name = "initial-commit", long)]
38     /// create an initial empty commit
39     flag_initial_commit: bool,
40     #[structopt(name = "perms", long = "shared")]
41     /// permissions to create the repository with
42     flag_shared: Option<String>,
43 }
44 
run(args: &Args) -> Result<(), Error>45 fn run(args: &Args) -> Result<(), Error> {
46     let mut path = PathBuf::from(&args.arg_directory);
47     let repo = if !args.flag_bare
48         && args.flag_template.is_none()
49         && args.flag_shared.is_none()
50         && args.flag_separate_git_dir.is_none()
51     {
52         Repository::init(&path)?
53     } else {
54         let mut opts = RepositoryInitOptions::new();
55         opts.bare(args.flag_bare);
56         if let Some(ref s) = args.flag_template {
57             opts.template_path(Path::new(s));
58         }
59 
60         // If you specified a separate git directory, then initialize
61         // the repository at that path and use the second path as the
62         // working directory of the repository (with a git-link file)
63         if let Some(ref s) = args.flag_separate_git_dir {
64             opts.workdir_path(&path);
65             path = PathBuf::from(s);
66         }
67 
68         if let Some(ref s) = args.flag_shared {
69             opts.mode(parse_shared(s)?);
70         }
71         Repository::init_opts(&path, &opts)?
72     };
73 
74     // Print a message to stdout like "git init" does
75     if !args.flag_quiet {
76         if args.flag_bare || args.flag_separate_git_dir.is_some() {
77             path = repo.path().to_path_buf();
78         } else {
79             path = repo.workdir().unwrap().to_path_buf();
80         }
81         println!("Initialized empty Git repository in {}", path.display());
82     }
83 
84     if args.flag_initial_commit {
85         create_initial_commit(&repo)?;
86         println!("Created empty initial commit");
87     }
88 
89     Ok(())
90 }
91 
92 /// Unlike regular "git init", this example shows how to create an initial empty
93 /// commit in the repository. This is the helper function that does that.
create_initial_commit(repo: &Repository) -> Result<(), Error>94 fn create_initial_commit(repo: &Repository) -> Result<(), Error> {
95     // First use the config to initialize a commit signature for the user.
96     let sig = repo.signature()?;
97 
98     // Now let's create an empty tree for this commit
99     let tree_id = {
100         let mut index = repo.index()?;
101 
102         // Outside of this example, you could call index.add_path()
103         // here to put actual files into the index. For our purposes, we'll
104         // leave it empty for now.
105 
106         index.write_tree()?
107     };
108 
109     let tree = repo.find_tree(tree_id)?;
110 
111     // Ready to create the initial commit.
112     //
113     // Normally creating a commit would involve looking up the current HEAD
114     // commit and making that be the parent of the initial commit, but here this
115     // is the first commit so there will be no parent.
116     repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[])?;
117 
118     Ok(())
119 }
120 
parse_shared(shared: &str) -> Result<RepositoryInitMode, Error>121 fn parse_shared(shared: &str) -> Result<RepositoryInitMode, Error> {
122     match shared {
123         "false" | "umask" => Ok(git2::RepositoryInitMode::SHARED_UMASK),
124         "true" | "group" => Ok(git2::RepositoryInitMode::SHARED_GROUP),
125         "all" | "world" => Ok(git2::RepositoryInitMode::SHARED_ALL),
126         _ => {
127             if shared.starts_with('0') {
128                 match u32::from_str_radix(&shared[1..], 8).ok() {
129                     Some(n) => Ok(RepositoryInitMode::from_bits_truncate(n)),
130                     None => Err(Error::from_str("invalid octal value for --shared")),
131                 }
132             } else {
133                 Err(Error::from_str("unknown value for --shared"))
134             }
135         }
136     }
137 }
138 
main()139 fn main() {
140     let args = Args::from_args();
141     match run(&args) {
142         Ok(()) => {}
143         Err(e) => println!("error: {}", e),
144     }
145 }
146