summaryrefslogtreecommitdiffstats
path: root/vendor/git2/examples/init.rs
blob: 3e447cbdeecd88df03236b7159778f309e8e1a00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 * libgit2 "init" example - shows how to initialize a new repo
 *
 * Written by the libgit2 contributors
 *
 * To the extent possible under law, the author(s) have dedicated all copyright
 * and related and neighboring rights to this software to the public domain
 * worldwide. This software is distributed without any warranty.
 *
 * You should have received a copy of the CC0 Public Domain Dedication along
 * with this software. If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */

#![deny(warnings)]

use git2::{Error, Repository, RepositoryInitMode, RepositoryInitOptions};
use std::path::{Path, PathBuf};
use structopt::StructOpt;

#[derive(StructOpt)]
struct Args {
    #[structopt(name = "directory")]
    arg_directory: String,
    #[structopt(name = "quiet", short, long)]
    /// don't print information to stdout
    flag_quiet: bool,
    #[structopt(name = "bare", long)]
    /// initialize a new bare repository
    flag_bare: bool,
    #[structopt(name = "dir", long = "template")]
    /// use <dir> as an initialization template
    flag_template: Option<String>,
    #[structopt(name = "separate-git-dir", long)]
    /// use <dir> as the .git directory
    flag_separate_git_dir: Option<String>,
    #[structopt(name = "initial-commit", long)]
    /// create an initial empty commit
    flag_initial_commit: bool,
    #[structopt(name = "perms", long = "shared")]
    /// permissions to create the repository with
    flag_shared: Option<String>,
}

fn run(args: &Args) -> Result<(), Error> {
    let mut path = PathBuf::from(&args.arg_directory);
    let repo = if !args.flag_bare
        && args.flag_template.is_none()
        && args.flag_shared.is_none()
        && args.flag_separate_git_dir.is_none()
    {
        Repository::init(&path)?
    } else {
        let mut opts = RepositoryInitOptions::new();
        opts.bare(args.flag_bare);
        if let Some(ref s) = args.flag_template {
            opts.template_path(Path::new(s));
        }

        // If you specified a separate git directory, then initialize
        // the repository at that path and use the second path as the
        // working directory of the repository (with a git-link file)
        if let Some(ref s) = args.flag_separate_git_dir {
            opts.workdir_path(&path);
            path = PathBuf::from(s);
        }

        if let Some(ref s) = args.flag_shared {
            opts.mode(parse_shared(s)?);
        }
        Repository::init_opts(&path, &opts)?
    };

    // Print a message to stdout like "git init" does
    if !args.flag_quiet {
        if args.flag_bare || args.flag_separate_git_dir.is_some() {
            path = repo.path().to_path_buf();
        } else {
            path = repo.workdir().unwrap().to_path_buf();
        }
        println!("Initialized empty Git repository in {}", path.display());
    }

    if args.flag_initial_commit {
        create_initial_commit(&repo)?;
        println!("Created empty initial commit");
    }

    Ok(())
}

/// Unlike regular "git init", this example shows how to create an initial empty
/// commit in the repository. This is the helper function that does that.
fn create_initial_commit(repo: &Repository) -> Result<(), Error> {
    // First use the config to initialize a commit signature for the user.
    let sig = repo.signature()?;

    // Now let's create an empty tree for this commit
    let tree_id = {
        let mut index = repo.index()?;

        // Outside of this example, you could call index.add_path()
        // here to put actual files into the index. For our purposes, we'll
        // leave it empty for now.

        index.write_tree()?
    };

    let tree = repo.find_tree(tree_id)?;

    // Ready to create the initial commit.
    //
    // Normally creating a commit would involve looking up the current HEAD
    // commit and making that be the parent of the initial commit, but here this
    // is the first commit so there will be no parent.
    repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[])?;

    Ok(())
}

fn parse_shared(shared: &str) -> Result<RepositoryInitMode, Error> {
    match shared {
        "false" | "umask" => Ok(git2::RepositoryInitMode::SHARED_UMASK),
        "true" | "group" => Ok(git2::RepositoryInitMode::SHARED_GROUP),
        "all" | "world" => Ok(git2::RepositoryInitMode::SHARED_ALL),
        _ => {
            if shared.starts_with('0') {
                match u32::from_str_radix(&shared[1..], 8).ok() {
                    Some(n) => Ok(RepositoryInitMode::from_bits_truncate(n)),
                    None => Err(Error::from_str("invalid octal value for --shared")),
                }
            } else {
                Err(Error::from_str("unknown value for --shared"))
            }
        }
    }
}

fn main() {
    let args = Args::from_args();
    match run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}