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