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 module libgit2_d.status;
8 
9 
10 private static import libgit2_d.diff;
11 private static import libgit2_d.strarray;
12 private static import libgit2_d.types;
13 
14 /**
15  * @file git2/status.h
16  * @brief Git file status routines
17  * @defgroup git_status Git file status routines
18  * @ingroup Git
19  * @{
20  */
21 extern (C):
22 nothrow @nogc:
23 public:
24 
25 /**
26  * Status flags for a single file.
27  *
28  * A combination of these values will be returned to indicate the status of
29  * a file.  Status compares the working directory, the index, and the
30  * current HEAD of the repository.  The `GIT_STATUS_INDEX` set of flags
31  * represents the status of file in the index relative to the HEAD, and the
32  * `GIT_STATUS_WT` set of flags represent the status of the file in the
33  * working directory relative to the index.
34  */
35 enum git_status_t
36 {
37 	GIT_STATUS_CURRENT = 0,
38 
39 	GIT_STATUS_INDEX_NEW = 1u << 0,
40 	GIT_STATUS_INDEX_MODIFIED = 1u << 1,
41 	GIT_STATUS_INDEX_DELETED = 1u << 2,
42 	GIT_STATUS_INDEX_RENAMED = 1u << 3,
43 	GIT_STATUS_INDEX_TYPECHANGE = 1u << 4,
44 
45 	GIT_STATUS_WT_NEW = 1u << 7,
46 	GIT_STATUS_WT_MODIFIED = 1u << 8,
47 	GIT_STATUS_WT_DELETED = 1u << 9,
48 	GIT_STATUS_WT_TYPECHANGE = 1u << 10,
49 	GIT_STATUS_WT_RENAMED = 1u << 11,
50 	GIT_STATUS_WT_UNREADABLE = 1u << 12,
51 
52 	GIT_STATUS_IGNORED = 1u << 14,
53 	GIT_STATUS_CONFLICTED = 1u << 15,
54 }
55 
56 /**
57  * Function pointer to receive status on individual files
58  *
59  * `path` is the relative path to the file from the root of the repository.
60  *
61  * `status_flags` is a combination of `git_status_t` values that apply.
62  *
63  * `payload` is the value you passed to the foreach function as payload.
64  */
65 alias git_status_cb = int function(const (char)* path, uint status_flags, void* payload);
66 
67 /**
68  * Select the files on which to report status.
69  *
70  * With `git_status_foreach_ext`, this will control which changes get
71  * callbacks.  With `git_status_list_new`, these will control which
72  * changes are included in the list.
73  *
74  * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default.  This roughly
75  *   matches `git status --porcelain` regarding which files are
76  *   included and in what order.
77  * - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
78  *   comparison, not looking at working directory changes.
79  * - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
80  *   working directory comparison, not comparing the index to the HEAD.
81  */
82 enum git_status_show_t
83 {
84 	GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
85 	GIT_STATUS_SHOW_INDEX_ONLY = 1,
86 	GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
87 }
88 
89 /**
90  * Flags to control status callbacks
91  *
92  * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
93  *   on untracked files.  These will only be made if the workdir files are
94  *   included in the status "show" option.
95  * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
96  *   Again, these callbacks will only be made if the workdir files are
97  *   included in the status "show" option.
98  * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
99  *   made even on unmodified files.
100  * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
101  *   skipped.  This only applies if there are no pending typechanges to
102  *   the submodule (either from or to another type).
103  * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
104  *   untracked directories should be included.  Normally if an entire
105  *   directory is new, then just the top-level directory is included (with
106  *   a trailing slash on the entry name).  This flag says to include all
107  *   of the individual files in the directory instead.
108  * - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
109  *   should be treated as a literal path, and not as a pathspec pattern.
110  * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
111  *   ignored directories should be included in the status.  This is like
112  *   doing `git ls-files -o -i --exclude-standard` with core git.
113  * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
114  *   should be processed between the head and the index and enables
115  *   the GIT_STATUS_INDEX_RENAMED as a possible status flag.
116  * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
117  *   detection should be run between the index and the working directory
118  *   and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
119  * - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
120  *   sensitivity for the file system and forces the output to be in
121  *   case-sensitive order
122  * - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
123  *   sensitivity for the file system and forces the output to be in
124  *   case-insensitive order
125  * - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
126  *   should include rewritten files
127  * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
128  *   doing a "soft" index reload (i.e. reloading the index data if the
129  *   file on disk has been modified outside libgit2).
130  * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
131  *   in the index for files that are unchanged but have out of date stat
132  *   information in the index.  It will result in less work being done on
133  *   subsequent calls to get status.  This is mutually exclusive with the
134  *   NO_REFRESH option.
135  *
136  * Calling `git_status_foreach()` is like calling the extended version
137  * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
138  * and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS.  Those options are bundled
139  * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
140  */
141 enum git_status_opt_t
142 {
143 	GIT_STATUS_OPT_INCLUDE_UNTRACKED = 1u << 0,
144 	GIT_STATUS_OPT_INCLUDE_IGNORED = 1u << 1,
145 	GIT_STATUS_OPT_INCLUDE_UNMODIFIED = 1u << 2,
146 	GIT_STATUS_OPT_EXCLUDE_SUBMODULES = 1u << 3,
147 	GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = 1u << 4,
148 	GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = 1u << 5,
149 	GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = 1u << 6,
150 	GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = 1u << 7,
151 	GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = 1u << 8,
152 	GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = 1u << 9,
153 	GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = 1u << 10,
154 	GIT_STATUS_OPT_RENAMES_FROM_REWRITES = 1u << 11,
155 	GIT_STATUS_OPT_NO_REFRESH = 1u << 12,
156 	GIT_STATUS_OPT_UPDATE_INDEX = 1u << 13,
157 	GIT_STATUS_OPT_INCLUDE_UNREADABLE = 1u << 14,
158 	GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = 1u << 15,
159 }
160 
161 enum GIT_STATUS_OPT_DEFAULTS = .git_status_opt_t.GIT_STATUS_OPT_INCLUDE_IGNORED | .git_status_opt_t.GIT_STATUS_OPT_INCLUDE_UNTRACKED | .git_status_opt_t.GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
162 
163 /**
164  * Options to control how `git_status_foreach_ext()` will issue callbacks.
165  *
166  * Initialize with `GIT_STATUS_OPTIONS_INIT`. Alternatively, you can
167  * use `git_status_options_init`.
168  */
169 struct git_status_options
170 {
171 	/**
172 	 * The version
173 	 */
174 	uint version_;
175 
176 	/**
177 	 * The `show` value is one of the `git_status_show_t` constants that
178 	 * control which files to scan and in what order.
179 	 */
180 	.git_status_show_t show;
181 
182 	/**
183 	 * The `flags` value is an OR'ed combination of the `git_status_opt_t`
184 	 * values above.
185 	 */
186 	uint flags;
187 
188 	/**
189 	 * The `pathspec` is an array of path patterns to match (using
190 	 * fnmatch-style matching), or just an array of paths to match exactly if
191 	 * `git_status_opt_t.GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified in the flags.
192 	 */
193 	libgit2_d.strarray.git_strarray pathspec;
194 
195 	/**
196 	 * The `baseline` is the tree to be used for comparison to the working directory
197 	 * and index; defaults to HEAD.
198 	 */
199 	libgit2_d.types.git_tree* baseline;
200 }
201 
202 enum GIT_STATUS_OPTIONS_VERSION = 1;
203 
204 pragma(inline, true)
205 pure nothrow @safe @nogc
206 .git_status_options GIT_STATUS_OPTIONS_INIT()
207 
208 	do
209 	{
210 		.git_status_options OUTPUT =
211 		{
212 			version_: .GIT_STATUS_OPTIONS_VERSION,
213 		};
214 
215 		return OUTPUT;
216 	}
217 
218 /**
219  * Initialize git_status_options structure
220  *
221  * Initializes a `git_status_options` with default values. Equivalent to
222  * creating an instance with `GIT_STATUS_OPTIONS_INIT`.
223  *
224  * Params:
225  *      opts = The `git_status_options` struct to initialize.
226  *      version = The struct version; pass `GIT_STATUS_OPTIONS_VERSION`.
227  *
228  * Returns: Zero on success; -1 on failure.
229  */
230 //GIT_EXTERN
231 int git_status_options_init(.git_status_options* opts, uint version_);
232 
233 /**
234  * A status entry, providing the differences between the file as it exists
235  * in HEAD and the index, and providing the differences between the index
236  * and the working directory.
237  *
238  * The `status` value provides the status flags for this file.
239  *
240  * The `head_to_index` value provides detailed information about the
241  * differences between the file in HEAD and the file in the index.
242  *
243  * The `index_to_workdir` value provides detailed information about the
244  * differences between the file in the index and the file in the
245  * working directory.
246  */
247 struct git_status_entry
248 {
249 	.git_status_t status;
250 	libgit2_d.diff.git_diff_delta* head_to_index;
251 	libgit2_d.diff.git_diff_delta* index_to_workdir;
252 }
253 
254 /**
255  * Gather file statuses and run a callback for each one.
256  *
257  * The callback is passed the path of the file, the status (a combination of
258  * the `git_status_t` values above) and the `payload` data pointer passed
259  * into this function.
260  *
261  * If the callback returns a non-zero value, this function will stop looping
262  * and return that value to caller.
263  *
264  * Params:
265  *      repo = A repository object
266  *      callback = The function to call on each file
267  *      payload = Pointer to pass through to callback function
268  *
269  * Returns: 0 on success, non-zero callback return value, or error code
270  */
271 //GIT_EXTERN
272 int git_status_foreach(libgit2_d.types.git_repository* repo, .git_status_cb callback, void* payload);
273 
274 /**
275  * Gather file status information and run callbacks as requested.
276  *
277  * This is an extended version of the `git_status_foreach()` API that
278  * allows for more granular control over which paths will be processed and
279  * in what order.  See the `git_status_options` structure for details
280  * about the additional controls that this makes available.
281  *
282  * Note that if a `pathspec` is given in the `git_status_options` to filter
283  * the status, then the results from rename detection (if you enable it) may
284  * not be accurate.  To do rename detection properly, this must be called
285  * with no `pathspec` so that all files can be considered.
286  *
287  * Params:
288  *      repo = Repository object
289  *      opts = Status options structure
290  *      callback = The function to call on each file
291  *      payload = Pointer to pass through to callback function
292  *
293  * Returns: 0 on success, non-zero callback return value, or error code
294  */
295 //GIT_EXTERN
296 int git_status_foreach_ext(libgit2_d.types.git_repository* repo, const (.git_status_options)* opts, .git_status_cb callback, void* payload);
297 
298 /**
299  * Get file status for a single file.
300  *
301  * This tries to get status for the filename that you give.  If no files
302  * match that name (in either the HEAD, index, or working directory), this
303  * returns git_error_code.GIT_ENOTFOUND.
304  *
305  * If the name matches multiple files (for example, if the `path` names a
306  * directory or if running on a case- insensitive filesystem and yet the
307  * HEAD has two entries that both match the path), then this returns
308  * git_error_code.GIT_EAMBIGUOUS because it cannot give correct results.
309  *
310  * This does not do any sort of rename detection.  Renames require a set of
311  * targets and because of the path filtering, there is not enough
312  * information to check renames correctly.  To check file status with rename
313  * detection, there is no choice but to do a full `git_status_list_new` and
314  * scan through looking for the path that you are interested in.
315  *
316  * Params:
317  *      status_flags = Output combination of git_status_t values for file
318  *      repo = A repository object
319  *      path = The exact path to retrieve status for relative to the repository working directory
320  *
321  * Returns: 0 on success, git_error_code.GIT_ENOTFOUND if the file is not found in the HEAD, index, and work tree, git_error_code.GIT_EAMBIGUOUS if `path` matches multiple files or if it refers to a folder, and -1 on other errors.
322  */
323 //GIT_EXTERN
324 int git_status_file(uint* status_flags, libgit2_d.types.git_repository* repo, const (char)* path);
325 
326 /**
327  * Gather file status information and populate the `git_status_list`.
328  *
329  * Note that if a `pathspec` is given in the `git_status_options` to filter
330  * the status, then the results from rename detection (if you enable it) may
331  * not be accurate.  To do rename detection properly, this must be called
332  * with no `pathspec` so that all files can be considered.
333  *
334  * Params:
335  *      out_ = Pointer to store the status results in
336  *      repo = Repository object
337  *      opts = Status options structure
338  *
339  * Returns: 0 on success or error code
340  */
341 //GIT_EXTERN
342 int git_status_list_new(libgit2_d.types.git_status_list** out_, libgit2_d.types.git_repository* repo, const (.git_status_options)* opts);
343 
344 /**
345  * Gets the count of status entries in this list.
346  *
347  * If there are no changes in status (at least according the options given
348  * when the status list was created), this can return 0.
349  *
350  * Params:
351  *      statuslist = Existing status list object
352  *
353  * Returns: the number of status entries
354  */
355 //GIT_EXTERN
356 size_t git_status_list_entrycount(libgit2_d.types.git_status_list* statuslist);
357 
358 /**
359  * Get a pointer to one of the entries in the status list.
360  *
361  * The entry is not modifiable and should not be freed.
362  *
363  * Params:
364  *      statuslist = Existing status list object
365  *      idx = Position of the entry
366  *
367  * Returns: Pointer to the entry; null if out of bounds
368  */
369 //GIT_EXTERN
370 const (.git_status_entry)* git_status_byindex(libgit2_d.types.git_status_list* statuslist, size_t idx);
371 
372 /**
373  * Free an existing status list
374  *
375  * Params:
376  *      statuslist = Existing status list object
377  */
378 //GIT_EXTERN
379 void git_status_list_free(libgit2_d.types.git_status_list* statuslist);
380 
381 /**
382  * Test if the ignore rules apply to a given file.
383  *
384  * This function checks the ignore rules to see if they would apply to the
385  * given file.  This indicates if the file would be ignored regardless of
386  * whether the file is already in the index or committed to the repository.
387  *
388  * One way to think of this is if you were to do "git add ." on the
389  * directory containing the file, would it be added or not?
390  *
391  * Params:
392  *      ignored = Boolean returning 0 if the file is not ignored, 1 if it is
393  *      repo = A repository object
394  *      path = The file to check ignores for, rooted at the repo's workdir.
395  *
396  * Returns: 0 if ignore rules could be processed for the file (regardless of whether it exists or not), or an error < 0 if they could not.
397  */
398 //GIT_EXTERN
399 int git_status_should_ignore(int* ignored, libgit2_d.types.git_repository* repo, const (char)* path);
400 
401 /** @} */