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