Skip to content

Commit e427d0a

Browse files
committed
cli: add hash-object command
Introduce a simple command that emulates `git hash-object`.
1 parent dcabef2 commit e427d0a

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

src/cli/cmd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name);
2626

2727
/* Commands */
2828
extern int cmd_cat_file(int argc, char **argv);
29+
extern int cmd_hash_object(int argc, char **argv);
2930
extern int cmd_help(int argc, char **argv);
3031

3132
#endif /* CLI_cmd_h__ */

src/cli/cmd_hash_object.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#include <git2.h>
9+
#include "cli.h"
10+
#include "cmd.h"
11+
12+
#include "futils.h"
13+
14+
#define COMMAND_NAME "hash-object"
15+
16+
static int show_help;
17+
static char *type_name;
18+
static int write_object, read_stdin, literally;
19+
static char **filenames;
20+
21+
static const cli_opt_spec opts[] = {
22+
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
23+
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
24+
"display help about the " COMMAND_NAME " command" },
25+
26+
{ CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
27+
CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
28+
{ CLI_OPT_TYPE_SWITCH, NULL, 'w', &write_object, 1,
29+
CLI_OPT_USAGE_DEFAULT, NULL, "write the object to the object database" },
30+
{ CLI_OPT_TYPE_SWITCH, "literally", 0, &literally, 1,
31+
CLI_OPT_USAGE_DEFAULT, NULL, "do not validate the object contents" },
32+
{ CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
33+
CLI_OPT_USAGE_REQUIRED, NULL, "read content from stdin" },
34+
{ CLI_OPT_TYPE_ARGS, "file", 0, &filenames, 0,
35+
CLI_OPT_USAGE_CHOICE, "file", "the file (or files) to read and hash" },
36+
{ 0 },
37+
};
38+
39+
static void print_help(void)
40+
{
41+
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
42+
printf("\n");
43+
44+
printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n");
45+
printf("\n");
46+
47+
printf("Options:\n");
48+
49+
cli_opt_help_fprint(stdout, opts);
50+
}
51+
52+
static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
53+
{
54+
git_oid oid;
55+
56+
if (!literally) {
57+
int valid = 0;
58+
59+
if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid)
60+
return cli_error_git();
61+
}
62+
63+
if (write_object) {
64+
if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0)
65+
return cli_error_git();
66+
} else {
67+
if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0)
68+
return cli_error_git();
69+
}
70+
71+
if (printf("%s\n", git_oid_tostr_s(&oid)) < 0)
72+
return cli_error_os();
73+
74+
return 0;
75+
}
76+
77+
int cmd_hash_object(int argc, char **argv)
78+
{
79+
git_repository *repo = NULL;
80+
git_odb *odb = NULL;
81+
git_str buf = GIT_STR_INIT;
82+
cli_opt invalid_opt;
83+
git_object_t type = GIT_OBJECT_BLOB;
84+
char **filename;
85+
int ret = 0;
86+
87+
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
88+
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
89+
90+
if (show_help) {
91+
print_help();
92+
return 0;
93+
}
94+
95+
if (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
96+
return cli_error_usage("invalid object type '%s'", type_name);
97+
98+
if (write_object &&
99+
(git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0 ||
100+
git_repository_odb(&odb, repo) < 0)) {
101+
ret = cli_error_git();
102+
goto done;
103+
}
104+
105+
/*
106+
* TODO: we're reading blobs, we shouldn't pull them all into main
107+
* memory, we should just stream them into the odb instead.
108+
* (Or create a `git_odb_writefile` API.)
109+
*/
110+
if (read_stdin) {
111+
if (git_futils_readbuffer_fd_full(&buf, fileno(stdin)) < 0) {
112+
ret = cli_error_git();
113+
goto done;
114+
}
115+
116+
if ((ret = hash_buf(odb, &buf, type)) != 0)
117+
goto done;
118+
} else {
119+
for (filename = filenames; *filename; filename++) {
120+
if (git_futils_readbuffer(&buf, *filename) < 0) {
121+
ret = cli_error_git();
122+
goto done;
123+
}
124+
125+
if ((ret = hash_buf(odb, &buf, type)) != 0)
126+
goto done;
127+
}
128+
}
129+
130+
done:
131+
git_str_dispose(&buf);
132+
git_odb_free(odb);
133+
git_repository_free(repo);
134+
return ret;
135+
}

src/cli/main.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ const cli_opt_spec cli_common_opts[] = {
2828
};
2929

3030
const cli_cmd_spec cli_cmds[] = {
31-
{ "cat-file", cmd_cat_file, "Display an object in the repository" },
32-
{ "help", cmd_help, "Display help information" },
31+
{ "cat-file", cmd_cat_file, "Display an object in the repository" },
32+
{ "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
33+
{ "help", cmd_help, "Display help information" },
3334
{ NULL }
3435
};
3536

0 commit comments

Comments
 (0)