]>
Commit | Line | Data |
---|---|---|
0ea41445 | 1 | /* |
6cb831bd | 2 | * libgit2 "init" example - shows how to initialize a new repo |
85c6730c | 3 | * |
6cb831bd BS |
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/>. | |
85c6730c BS |
13 | */ |
14 | ||
15 | #include "common.h" | |
16 | ||
17 | /** | |
0ea41445 RB |
18 | * This is a sample program that is similar to "git init". See the |
19 | * documentation for that (try "git help init") to understand what this | |
20 | * program is emulating. | |
21 | * | |
22 | * This demonstrates using the libgit2 APIs to initialize a new repository. | |
23 | * | |
24 | * This also contains a special additional option that regular "git init" | |
25 | * does not support which is "--initial-commit" to make a first empty commit. | |
26 | * That is demonstrated in the "create_initial_commit" helper function. | |
0ea41445 RB |
27 | */ |
28 | ||
85c6730c | 29 | /** Forward declarations of helpers */ |
0c9c969a | 30 | struct init_opts { |
a8422f92 BS |
31 | int no_options; |
32 | int quiet; | |
33 | int bare; | |
34 | int initial_commit; | |
35 | uint32_t shared; | |
36 | const char *template; | |
37 | const char *gitdir; | |
38 | const char *dir; | |
39 | }; | |
944c1589 | 40 | static void create_initial_commit(git_repository *repo); |
0c9c969a | 41 | static void parse_opts(struct init_opts *o, int argc, char *argv[]); |
944c1589 | 42 | |
0c9c969a | 43 | int lg2_init(git_repository *repo, int argc, char *argv[]) |
944c1589 | 44 | { |
0c9c969a | 45 | struct init_opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 }; |
944c1589 | 46 | |
a8422f92 | 47 | parse_opts(&o, argc, argv); |
944c1589 | 48 | |
85c6730c | 49 | /* Initialize repository. */ |
944c1589 | 50 | |
a8422f92 | 51 | if (o.no_options) { |
85c6730c BS |
52 | /** |
53 | * No options were specified, so let's demonstrate the default | |
944c1589 RB |
54 | * simple case of git_repository_init() API usage... |
55 | */ | |
7cc3c9bf BS |
56 | check_lg2(git_repository_init(&repo, o.dir, 0), |
57 | "Could not initialize repository", NULL); | |
944c1589 RB |
58 | } |
59 | else { | |
85c6730c BS |
60 | /** |
61 | * Some command line options were specified, so we'll use the | |
944c1589 RB |
62 | * extended init API to handle them |
63 | */ | |
a8422f92 BS |
64 | git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT; |
65 | initopts.flags = GIT_REPOSITORY_INIT_MKPATH; | |
944c1589 | 66 | |
a8422f92 BS |
67 | if (o.bare) |
68 | initopts.flags |= GIT_REPOSITORY_INIT_BARE; | |
944c1589 | 69 | |
a8422f92 BS |
70 | if (o.template) { |
71 | initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; | |
72 | initopts.template_path = o.template; | |
944c1589 RB |
73 | } |
74 | ||
a8422f92 | 75 | if (o.gitdir) { |
85c6730c BS |
76 | /** |
77 | * If you specified a separate git directory, then initialize | |
944c1589 RB |
78 | * the repository at that path and use the second path as the |
79 | * working directory of the repository (with a git-link file) | |
80 | */ | |
a8422f92 BS |
81 | initopts.workdir_path = o.dir; |
82 | o.dir = o.gitdir; | |
944c1589 RB |
83 | } |
84 | ||
a8422f92 BS |
85 | if (o.shared != 0) |
86 | initopts.mode = o.shared; | |
944c1589 | 87 | |
7cc3c9bf BS |
88 | check_lg2(git_repository_init_ext(&repo, o.dir, &initopts), |
89 | "Could not initialize repository", NULL); | |
944c1589 RB |
90 | } |
91 | ||
85c6730c | 92 | /** Print a message to stdout like "git init" does. */ |
0ea41445 | 93 | |
a8422f92 BS |
94 | if (!o.quiet) { |
95 | if (o.bare || o.gitdir) | |
96 | o.dir = git_repository_path(repo); | |
944c1589 | 97 | else |
a8422f92 | 98 | o.dir = git_repository_workdir(repo); |
944c1589 | 99 | |
a8422f92 | 100 | printf("Initialized empty Git repository in %s\n", o.dir); |
944c1589 RB |
101 | } |
102 | ||
85c6730c BS |
103 | /** |
104 | * As an extension to the basic "git init" command, this example | |
944c1589 RB |
105 | * gives the option to create an empty initial commit. This is |
106 | * mostly to demonstrate what it takes to do that, but also some | |
107 | * people like to have that empty base commit in their repo. | |
108 | */ | |
a8422f92 | 109 | if (o.initial_commit) { |
944c1589 RB |
110 | create_initial_commit(repo); |
111 | printf("Created empty initial commit\n"); | |
112 | } | |
113 | ||
114 | git_repository_free(repo); | |
944c1589 RB |
115 | |
116 | return 0; | |
117 | } | |
118 | ||
85c6730c BS |
119 | /** |
120 | * Unlike regular "git init", this example shows how to create an initial | |
0ea41445 RB |
121 | * empty commit in the repository. This is the helper function that does |
122 | * that. | |
123 | */ | |
944c1589 RB |
124 | static void create_initial_commit(git_repository *repo) |
125 | { | |
126 | git_signature *sig; | |
127 | git_index *index; | |
128 | git_oid tree_id, commit_id; | |
129 | git_tree *tree; | |
130 | ||
85c6730c | 131 | /** First use the config to initialize a commit signature for the user. */ |
944c1589 RB |
132 | |
133 | if (git_signature_default(&sig, repo) < 0) | |
a8422f92 BS |
134 | fatal("Unable to create a commit signature.", |
135 | "Perhaps 'user.name' and 'user.email' are not set"); | |
944c1589 RB |
136 | |
137 | /* Now let's create an empty tree for this commit */ | |
138 | ||
139 | if (git_repository_index(&index, repo) < 0) | |
a8422f92 | 140 | fatal("Could not open repository index", NULL); |
944c1589 | 141 | |
85c6730c BS |
142 | /** |
143 | * Outside of this example, you could call git_index_add_bypath() | |
944c1589 RB |
144 | * here to put actual files into the index. For our purposes, we'll |
145 | * leave it empty for now. | |
146 | */ | |
147 | ||
148 | if (git_index_write_tree(&tree_id, index) < 0) | |
a8422f92 | 149 | fatal("Unable to write initial tree from index", NULL); |
944c1589 RB |
150 | |
151 | git_index_free(index); | |
152 | ||
153 | if (git_tree_lookup(&tree, repo, &tree_id) < 0) | |
a8422f92 | 154 | fatal("Could not look up initial tree", NULL); |
944c1589 | 155 | |
85c6730c BS |
156 | /** |
157 | * Ready to create the initial commit. | |
944c1589 RB |
158 | * |
159 | * Normally creating a commit would involve looking up the current | |
160 | * HEAD commit and making that be the parent of the initial commit, | |
161 | * but here this is the first commit so there will be no parent. | |
162 | */ | |
163 | ||
164 | if (git_commit_create_v( | |
165 | &commit_id, repo, "HEAD", sig, sig, | |
166 | NULL, "Initial commit", tree, 0) < 0) | |
a8422f92 | 167 | fatal("Could not create the initial commit", NULL); |
944c1589 | 168 | |
85c6730c | 169 | /** Clean up so we don't leak memory. */ |
944c1589 RB |
170 | |
171 | git_tree_free(tree); | |
172 | git_signature_free(sig); | |
173 | } | |
a8422f92 BS |
174 | |
175 | static void usage(const char *error, const char *arg) | |
176 | { | |
177 | fprintf(stderr, "error: %s '%s'\n", error, arg); | |
178 | fprintf(stderr, | |
179 | "usage: init [-q | --quiet] [--bare] [--template=<dir>]\n" | |
180 | " [--shared[=perms]] [--initial-commit]\n" | |
181 | " [--separate-git-dir] <directory>\n"); | |
182 | exit(1); | |
183 | } | |
184 | ||
85c6730c | 185 | /** Parse the tail of the --shared= argument. */ |
a8422f92 BS |
186 | static uint32_t parse_shared(const char *shared) |
187 | { | |
188 | if (!strcmp(shared, "false") || !strcmp(shared, "umask")) | |
189 | return GIT_REPOSITORY_INIT_SHARED_UMASK; | |
190 | ||
191 | else if (!strcmp(shared, "true") || !strcmp(shared, "group")) | |
192 | return GIT_REPOSITORY_INIT_SHARED_GROUP; | |
193 | ||
194 | else if (!strcmp(shared, "all") || !strcmp(shared, "world") || | |
195 | !strcmp(shared, "everybody")) | |
196 | return GIT_REPOSITORY_INIT_SHARED_ALL; | |
197 | ||
198 | else if (shared[0] == '0') { | |
199 | long val; | |
200 | char *end = NULL; | |
201 | val = strtol(shared + 1, &end, 8); | |
202 | if (end == shared + 1 || *end != 0) | |
203 | usage("invalid octal value for --shared", shared); | |
204 | return (uint32_t)val; | |
205 | } | |
206 | ||
207 | else | |
208 | usage("unknown value for --shared", shared); | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
0c9c969a | 213 | static void parse_opts(struct init_opts *o, int argc, char *argv[]) |
a8422f92 BS |
214 | { |
215 | struct args_info args = ARGS_INFO_INIT; | |
216 | const char *sharedarg; | |
217 | ||
85c6730c | 218 | /** Process arguments. */ |
a8422f92 BS |
219 | |
220 | for (args.pos = 1; args.pos < argc; ++args.pos) { | |
221 | char *a = argv[args.pos]; | |
222 | ||
223 | if (a[0] == '-') | |
224 | o->no_options = 0; | |
225 | ||
226 | if (a[0] != '-') { | |
227 | if (o->dir != NULL) | |
228 | usage("extra argument", a); | |
229 | o->dir = a; | |
230 | } | |
231 | else if (!strcmp(a, "-q") || !strcmp(a, "--quiet")) | |
232 | o->quiet = 1; | |
233 | else if (!strcmp(a, "--bare")) | |
234 | o->bare = 1; | |
235 | else if (!strcmp(a, "--shared")) | |
236 | o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP; | |
237 | else if (!strcmp(a, "--initial-commit")) | |
238 | o->initial_commit = 1; | |
239 | else if (match_str_arg(&sharedarg, &args, "--shared")) | |
240 | o->shared = parse_shared(sharedarg); | |
241 | else if (!match_str_arg(&o->template, &args, "--template") || | |
242 | !match_str_arg(&o->gitdir, &args, "--separate-git-dir")) | |
243 | usage("unknown option", a); | |
244 | } | |
245 | ||
246 | if (!o->dir) | |
0c9c969a | 247 | usage("must specify directory to init", ""); |
a8422f92 | 248 | } |