Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/init-db.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * GIT - The information manager from hell
3
 *
4
 * Copyright (C) Linus Torvalds, 2005
5
 */
6
#include "builtin.h"
7
#include "abspath.h"
8
#include "environment.h"
9
#include "gettext.h"
10
#include "object-file.h"
11
#include "parse-options.h"
12
#include "path.h"
13
#include "refs.h"
14
#include "repository.h"
15
#include "setup.h"
16
#include "strbuf.h"
17
18
static int guess_repository_type(const char *git_dir)
19
0
{
20
0
  const char *slash;
21
0
  char *cwd;
22
0
  int cwd_is_git_dir;
23
24
  /*
25
   * "GIT_DIR=. git init" is always bare.
26
   * "GIT_DIR=`pwd` git init" too.
27
   */
28
0
  if (!strcmp(".", git_dir))
29
0
    return 1;
30
0
  cwd = xgetcwd();
31
0
  cwd_is_git_dir = !strcmp(git_dir, cwd);
32
0
  free(cwd);
33
0
  if (cwd_is_git_dir)
34
0
    return 1;
35
  /*
36
   * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
37
   */
38
0
  if (!strcmp(git_dir, ".git"))
39
0
    return 0;
40
0
  slash = strrchr(git_dir, '/');
41
0
  if (slash && !strcmp(slash, "/.git"))
42
0
    return 0;
43
44
  /*
45
   * Otherwise it is often bare.  At this point
46
   * we are just guessing.
47
   */
48
0
  return 1;
49
0
}
50
51
static int shared_callback(const struct option *opt, const char *arg, int unset)
52
0
{
53
0
  BUG_ON_OPT_NEG(unset);
54
0
  *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
55
0
  return 0;
56
0
}
57
58
static const char *const init_db_usage[] = {
59
  N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
60
     "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
61
     "         [--ref-format=<format>]\n"
62
     "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
63
     "         [--shared[=<permissions>]] [<directory>]"),
64
  NULL
65
};
66
67
/*
68
 * If you want to, you can share the DB area with any number of branches.
69
 * That has advantages: you can save space by sharing all the SHA1 objects.
70
 * On the other hand, it might just make lookup slower and messier. You
71
 * be the judge.  The default case is to have one DB per managed directory.
72
 */
73
int cmd_init_db(int argc, const char **argv, const char *prefix)
74
0
{
75
0
  const char *git_dir;
76
0
  const char *real_git_dir = NULL;
77
0
  const char *work_tree;
78
0
  const char *template_dir = NULL;
79
0
  unsigned int flags = 0;
80
0
  const char *object_format = NULL;
81
0
  const char *ref_format = NULL;
82
0
  const char *initial_branch = NULL;
83
0
  int hash_algo = GIT_HASH_UNKNOWN;
84
0
  enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
85
0
  int init_shared_repository = -1;
86
0
  const struct option init_db_options[] = {
87
0
    OPT_STRING(0, "template", &template_dir, N_("template-directory"),
88
0
        N_("directory from which templates will be used")),
89
0
    OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
90
0
        N_("create a bare repository"), 1),
91
0
    { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
92
0
      N_("permissions"),
93
0
      N_("specify that the git repository is to be shared amongst several users"),
94
0
      PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
95
0
    OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
96
0
    OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
97
0
         N_("separate git dir from working tree")),
98
0
    OPT_STRING('b', "initial-branch", &initial_branch, N_("name"),
99
0
         N_("override the name of the initial branch")),
100
0
    OPT_STRING(0, "object-format", &object_format, N_("hash"),
101
0
         N_("specify the hash algorithm to use")),
102
0
    OPT_STRING(0, "ref-format", &ref_format, N_("format"),
103
0
         N_("specify the reference format to use")),
104
0
    OPT_END()
105
0
  };
106
107
0
  argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
108
109
0
  if (real_git_dir && is_bare_repository_cfg == 1)
110
0
    die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare");
111
112
0
  if (real_git_dir && !is_absolute_path(real_git_dir))
113
0
    real_git_dir = real_pathdup(real_git_dir, 1);
114
115
0
  if (template_dir && *template_dir && !is_absolute_path(template_dir)) {
116
0
    template_dir = absolute_pathdup(template_dir);
117
0
    UNLEAK(template_dir);
118
0
  }
119
120
0
  if (argc == 1) {
121
0
    int mkdir_tried = 0;
122
0
  retry:
123
0
    if (chdir(argv[0]) < 0) {
124
0
      if (!mkdir_tried) {
125
0
        int saved;
126
        /*
127
         * At this point we haven't read any configuration,
128
         * and we know shared_repository should always be 0;
129
         * but just in case we play safe.
130
         */
131
0
        saved = get_shared_repository();
132
0
        set_shared_repository(0);
133
0
        switch (safe_create_leading_directories_const(argv[0])) {
134
0
        case SCLD_OK:
135
0
        case SCLD_PERMS:
136
0
          break;
137
0
        case SCLD_EXISTS:
138
0
          errno = EEXIST;
139
          /* fallthru */
140
0
        default:
141
0
          die_errno(_("cannot mkdir %s"), argv[0]);
142
0
          break;
143
0
        }
144
0
        set_shared_repository(saved);
145
0
        if (mkdir(argv[0], 0777) < 0)
146
0
          die_errno(_("cannot mkdir %s"), argv[0]);
147
0
        mkdir_tried = 1;
148
0
        goto retry;
149
0
      }
150
0
      die_errno(_("cannot chdir to %s"), argv[0]);
151
0
    }
152
0
  } else if (0 < argc) {
153
0
    usage(init_db_usage[0]);
154
0
  }
155
0
  if (is_bare_repository_cfg == 1) {
156
0
    char *cwd = xgetcwd();
157
0
    setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0);
158
0
    free(cwd);
159
0
  }
160
161
0
  if (object_format) {
162
0
    hash_algo = hash_algo_by_name(object_format);
163
0
    if (hash_algo == GIT_HASH_UNKNOWN)
164
0
      die(_("unknown hash algorithm '%s'"), object_format);
165
0
  }
166
167
0
  if (ref_format) {
168
0
    ref_storage_format = ref_storage_format_by_name(ref_format);
169
0
    if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
170
0
      die(_("unknown ref storage format '%s'"), ref_format);
171
0
  }
172
173
0
  if (init_shared_repository != -1)
174
0
    set_shared_repository(init_shared_repository);
175
176
  /*
177
   * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
178
   * without --bare.  Catch the error early.
179
   */
180
0
  git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
181
0
  work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
182
0
  if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
183
0
    die(_("%s (or --work-tree=<directory>) not allowed without "
184
0
        "specifying %s (or --git-dir=<directory>)"),
185
0
        GIT_WORK_TREE_ENVIRONMENT,
186
0
        GIT_DIR_ENVIRONMENT);
187
188
  /*
189
   * Set up the default .git directory contents
190
   */
191
0
  if (!git_dir)
192
0
    git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
193
194
  /*
195
   * When --separate-git-dir is used inside a linked worktree, take
196
   * care to ensure that the common .git/ directory is relocated, not
197
   * the worktree-specific .git/worktrees/<id>/ directory.
198
   */
199
0
  if (real_git_dir) {
200
0
    int err;
201
0
    const char *p;
202
0
    struct strbuf sb = STRBUF_INIT;
203
204
0
    p = read_gitfile_gently(git_dir, &err);
205
0
    if (p && get_common_dir(&sb, p)) {
206
0
      struct strbuf mainwt = STRBUF_INIT;
207
208
0
      strbuf_addbuf(&mainwt, &sb);
209
0
      strbuf_strip_suffix(&mainwt, "/.git");
210
0
      if (chdir(mainwt.buf) < 0)
211
0
        die_errno(_("cannot chdir to %s"), mainwt.buf);
212
0
      strbuf_release(&mainwt);
213
0
      git_dir = strbuf_detach(&sb, NULL);
214
0
    }
215
0
    strbuf_release(&sb);
216
0
  }
217
218
0
  if (is_bare_repository_cfg < 0)
219
0
    is_bare_repository_cfg = guess_repository_type(git_dir);
220
221
0
  if (!is_bare_repository_cfg) {
222
0
    const char *git_dir_parent = strrchr(git_dir, '/');
223
0
    if (git_dir_parent) {
224
0
      char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
225
0
      git_work_tree_cfg = real_pathdup(rel, 1);
226
0
      free(rel);
227
0
    }
228
0
    if (!git_work_tree_cfg)
229
0
      git_work_tree_cfg = xgetcwd();
230
0
    if (work_tree)
231
0
      set_git_work_tree(work_tree);
232
0
    else
233
0
      set_git_work_tree(git_work_tree_cfg);
234
0
    if (access(get_git_work_tree(), X_OK))
235
0
      die_errno (_("Cannot access work tree '%s'"),
236
0
           get_git_work_tree());
237
0
  }
238
0
  else {
239
0
    if (real_git_dir)
240
0
      die(_("--separate-git-dir incompatible with bare repository"));
241
0
    if (work_tree)
242
0
      set_git_work_tree(work_tree);
243
0
  }
244
245
0
  UNLEAK(real_git_dir);
246
0
  UNLEAK(git_dir);
247
0
  UNLEAK(work_tree);
248
249
0
  flags |= INIT_DB_EXIST_OK;
250
0
  return init_db(git_dir, real_git_dir, template_dir, hash_algo,
251
0
           ref_storage_format, initial_branch,
252
0
           init_shared_repository, flags);
253
0
}