1 /*
2  * libgit2 "ls-files" example - shows how to view all files currently in the index
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 #include "common.h"
16 
17 /**
18  * This example demonstrates the libgit2 index APIs to roughly
19  * simulate the output of `git ls-files`.
20  * `git ls-files` has many options and this currently does not show them.
21  *
22  * `git ls-files` base command shows all paths in the index at that time.
23  * This includes staged and committed files, but unstaged files will not display.
24  *
25  * This currently supports the default behavior and the `--error-unmatch` option.
26  */
27 
28 struct ls_options {
29 	int error_unmatch;
30 	char *files[1024];
31 	size_t file_count;
32 };
33 
usage(const char * message,const char * arg)34 static void usage(const char *message, const char *arg)
35 {
36 	if (message && arg)
37 		fprintf(stderr, "%s: %s\n", message, arg);
38 	else if (message)
39 		fprintf(stderr, "%s\n", message);
40 	fprintf(stderr, "usage: ls-files [--error-unmatch] [--] [<file>...]\n");
41 	exit(1);
42 }
43 
parse_options(struct ls_options * opts,int argc,char * argv[])44 static int parse_options(struct ls_options *opts, int argc, char *argv[])
45 {
46 	int parsing_files = 0;
47 	int i;
48 
49 	memset(opts, 0, sizeof(struct ls_options));
50 
51 	if (argc < 2)
52 		return 0;
53 
54 	for (i = 1; i < argc; ++i) {
55 		char *a = argv[i];
56 
57 		/* if it doesn't start with a '-' or is after the '--' then it is a file */
58 		if (a[0] != '-' || parsing_files) {
59 			parsing_files = 1;
60 
61 			/* watch for overflows (just in case) */
62 			if (opts->file_count == 1024) {
63 				fprintf(stderr, "ls-files can only support 1024 files at this time.\n");
64 				return -1;
65 			}
66 
67 			opts->files[opts->file_count++] = a;
68 		} else if (!strcmp(a, "--")) {
69 			parsing_files = 1;
70 		} else if (!strcmp(a, "--error-unmatch")) {
71 			opts->error_unmatch = 1;
72 		} else {
73 			usage("Unsupported argument", a);
74 			return -1;
75 		}
76 	}
77 
78 	return 0;
79 }
80 
print_paths(struct ls_options * opts,git_index * index)81 static int print_paths(struct ls_options *opts, git_index *index)
82 {
83 	size_t i;
84 	const git_index_entry *entry;
85 
86 	/* if there are no files explicitly listed by the user print all entries in the index */
87 	if (opts->file_count == 0) {
88 		size_t entry_count = git_index_entrycount(index);
89 
90 		for (i = 0; i < entry_count; i++) {
91 			entry = git_index_get_byindex(index, i);
92 			puts(entry->path);
93 		}
94 		return 0;
95 	}
96 
97 	/* loop through the files found in the args and print them if they exist */
98 	for (i = 0; i < opts->file_count; ++i) {
99 		const char *path = opts->files[i];
100 
101 		if ((entry = git_index_get_bypath(index, path, GIT_INDEX_STAGE_NORMAL)) != NULL) {
102 			puts(path);
103 		} else if (opts->error_unmatch) {
104 			fprintf(stderr, "error: pathspec '%s' did not match any file(s) known to git.\n", path);
105 			fprintf(stderr, "Did you forget to 'git add'?\n");
106 			return -1;
107 		}
108 	}
109 
110 	return 0;
111 }
112 
lg2_ls_files(git_repository * repo,int argc,char * argv[])113 int lg2_ls_files(git_repository *repo, int argc, char *argv[])
114 {
115 	git_index *index = NULL;
116 	struct ls_options opts;
117 	int error;
118 
119 	if ((error = parse_options(&opts, argc, argv)) < 0)
120 		return error;
121 
122 	if ((error = git_repository_index(&index, repo)) < 0)
123 		goto cleanup;
124 
125 	error = print_paths(&opts, index);
126 
127 cleanup:
128 	git_index_free(index);
129 
130 	return error;
131 }
132