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 /* @} */