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