pre-commit-ex 4.5.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. pre_commit/__init__.py +0 -0
  2. pre_commit/__main__.py +7 -0
  3. pre_commit/all_languages.py +50 -0
  4. pre_commit/clientlib.py +551 -0
  5. pre_commit/color.py +109 -0
  6. pre_commit/commands/__init__.py +0 -0
  7. pre_commit/commands/autoupdate.py +215 -0
  8. pre_commit/commands/clean.py +16 -0
  9. pre_commit/commands/gc.py +98 -0
  10. pre_commit/commands/hazmat.py +95 -0
  11. pre_commit/commands/hook_impl.py +272 -0
  12. pre_commit/commands/init_templatedir.py +39 -0
  13. pre_commit/commands/install_uninstall.py +167 -0
  14. pre_commit/commands/migrate_config.py +135 -0
  15. pre_commit/commands/run.py +474 -0
  16. pre_commit/commands/sample_config.py +18 -0
  17. pre_commit/commands/try_repo.py +77 -0
  18. pre_commit/commands/validate_config.py +18 -0
  19. pre_commit/commands/validate_manifest.py +18 -0
  20. pre_commit/constants.py +13 -0
  21. pre_commit/envcontext.py +62 -0
  22. pre_commit/error_handler.py +81 -0
  23. pre_commit/errors.py +5 -0
  24. pre_commit/file_lock.py +75 -0
  25. pre_commit/git.py +245 -0
  26. pre_commit/hook.py +60 -0
  27. pre_commit/lang_base.py +196 -0
  28. pre_commit/languages/__init__.py +0 -0
  29. pre_commit/languages/conda.py +77 -0
  30. pre_commit/languages/coursier.py +76 -0
  31. pre_commit/languages/dart.py +97 -0
  32. pre_commit/languages/docker.py +181 -0
  33. pre_commit/languages/docker_image.py +32 -0
  34. pre_commit/languages/dotnet.py +111 -0
  35. pre_commit/languages/fail.py +27 -0
  36. pre_commit/languages/golang.py +161 -0
  37. pre_commit/languages/haskell.py +56 -0
  38. pre_commit/languages/julia.py +133 -0
  39. pre_commit/languages/lua.py +75 -0
  40. pre_commit/languages/node.py +110 -0
  41. pre_commit/languages/perl.py +50 -0
  42. pre_commit/languages/pygrep.py +133 -0
  43. pre_commit/languages/python.py +228 -0
  44. pre_commit/languages/r.py +278 -0
  45. pre_commit/languages/ruby.py +145 -0
  46. pre_commit/languages/rust.py +160 -0
  47. pre_commit/languages/swift.py +50 -0
  48. pre_commit/languages/unsupported.py +10 -0
  49. pre_commit/languages/unsupported_script.py +32 -0
  50. pre_commit/logging_handler.py +42 -0
  51. pre_commit/main.py +472 -0
  52. pre_commit/meta_hooks/__init__.py +0 -0
  53. pre_commit/meta_hooks/check_hooks_apply.py +43 -0
  54. pre_commit/meta_hooks/check_useless_excludes.py +83 -0
  55. pre_commit/meta_hooks/identity.py +17 -0
  56. pre_commit/output.py +33 -0
  57. pre_commit/parse_shebang.py +85 -0
  58. pre_commit/prefix.py +18 -0
  59. pre_commit/repository.py +237 -0
  60. pre_commit/resources/__init__.py +0 -0
  61. pre_commit/resources/empty_template_.npmignore +1 -0
  62. pre_commit/resources/empty_template_Cargo.toml +7 -0
  63. pre_commit/resources/empty_template_LICENSE.renv +7 -0
  64. pre_commit/resources/empty_template_Makefile.PL +6 -0
  65. pre_commit/resources/empty_template_activate.R +440 -0
  66. pre_commit/resources/empty_template_environment.yml +9 -0
  67. pre_commit/resources/empty_template_go.mod +1 -0
  68. pre_commit/resources/empty_template_main.go +3 -0
  69. pre_commit/resources/empty_template_main.rs +1 -0
  70. pre_commit/resources/empty_template_package.json +4 -0
  71. pre_commit/resources/empty_template_pre-commit-package-dev-1.rockspec +12 -0
  72. pre_commit/resources/empty_template_pre_commit_placeholder_package.gemspec +6 -0
  73. pre_commit/resources/empty_template_pubspec.yaml +4 -0
  74. pre_commit/resources/empty_template_renv.lock +20 -0
  75. pre_commit/resources/empty_template_setup.py +4 -0
  76. pre_commit/resources/hook-tmpl +20 -0
  77. pre_commit/resources/rbenv.tar.gz +0 -0
  78. pre_commit/resources/ruby-build.tar.gz +0 -0
  79. pre_commit/resources/ruby-download.tar.gz +0 -0
  80. pre_commit/staged_files_only.py +113 -0
  81. pre_commit/store.py +235 -0
  82. pre_commit/util.py +239 -0
  83. pre_commit/xargs.py +184 -0
  84. pre_commit/yaml.py +19 -0
  85. pre_commit/yaml_rewrite.py +52 -0
  86. pre_commit_ex-4.5.1.dist-info/METADATA +63 -0
  87. pre_commit_ex-4.5.1.dist-info/RECORD +91 -0
  88. pre_commit_ex-4.5.1.dist-info/WHEEL +6 -0
  89. pre_commit_ex-4.5.1.dist-info/entry_points.txt +2 -0
  90. pre_commit_ex-4.5.1.dist-info/licenses/LICENSE +19 -0
  91. pre_commit_ex-4.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,440 @@
1
+
2
+ local({
3
+
4
+ # the requested version of renv
5
+ version <- "0.12.5"
6
+
7
+ # the project directory
8
+ project <- getwd()
9
+
10
+ # avoid recursion
11
+ if (!is.na(Sys.getenv("RENV_R_INITIALIZING", unset = NA)))
12
+ return(invisible(TRUE))
13
+
14
+ # signal that we're loading renv during R startup
15
+ Sys.setenv("RENV_R_INITIALIZING" = "true")
16
+ on.exit(Sys.unsetenv("RENV_R_INITIALIZING"), add = TRUE)
17
+
18
+ # signal that we've consented to use renv
19
+ options(renv.consent = TRUE)
20
+
21
+ # load the 'utils' package eagerly -- this ensures that renv shims, which
22
+ # mask 'utils' packages, will come first on the search path
23
+ library(utils, lib.loc = .Library)
24
+
25
+ # check to see if renv has already been loaded
26
+ if ("renv" %in% loadedNamespaces()) {
27
+
28
+ # if renv has already been loaded, and it's the requested version of renv,
29
+ # nothing to do
30
+ spec <- .getNamespaceInfo(.getNamespace("renv"), "spec")
31
+ if (identical(spec[["version"]], version))
32
+ return(invisible(TRUE))
33
+
34
+ # otherwise, unload and attempt to load the correct version of renv
35
+ unloadNamespace("renv")
36
+
37
+ }
38
+
39
+ # load bootstrap tools
40
+ bootstrap <- function(version, library) {
41
+
42
+ # attempt to download renv
43
+ tarball <- tryCatch(renv_bootstrap_download(version), error = identity)
44
+ if (inherits(tarball, "error"))
45
+ stop("failed to download renv ", version)
46
+
47
+ # now attempt to install
48
+ status <- tryCatch(renv_bootstrap_install(version, tarball, library), error = identity)
49
+ if (inherits(status, "error"))
50
+ stop("failed to install renv ", version)
51
+
52
+ }
53
+
54
+ renv_bootstrap_tests_running <- function() {
55
+ getOption("renv.tests.running", default = FALSE)
56
+ }
57
+
58
+ renv_bootstrap_repos <- function() {
59
+
60
+ # check for repos override
61
+ repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA)
62
+ if (!is.na(repos))
63
+ return(repos)
64
+
65
+ # if we're testing, re-use the test repositories
66
+ if (renv_bootstrap_tests_running())
67
+ return(getOption("renv.tests.repos"))
68
+
69
+ # retrieve current repos
70
+ repos <- getOption("repos")
71
+
72
+ # ensure @CRAN@ entries are resolved
73
+ repos[repos == "@CRAN@"] <- "https://cloud.r-project.org"
74
+
75
+ # add in renv.bootstrap.repos if set
76
+ default <- c(CRAN = "https://cloud.r-project.org")
77
+ extra <- getOption("renv.bootstrap.repos", default = default)
78
+ repos <- c(repos, extra)
79
+
80
+ # remove duplicates that might've snuck in
81
+ dupes <- duplicated(repos) | duplicated(names(repos))
82
+ repos[!dupes]
83
+
84
+ }
85
+
86
+ renv_bootstrap_download <- function(version) {
87
+
88
+ # if the renv version number has 4 components, assume it must
89
+ # be retrieved via github
90
+ nv <- numeric_version(version)
91
+ components <- unclass(nv)[[1]]
92
+
93
+ methods <- if (length(components) == 4L) {
94
+ list(
95
+ renv_bootstrap_download_github
96
+ )
97
+ } else {
98
+ list(
99
+ renv_bootstrap_download_cran_latest,
100
+ renv_bootstrap_download_cran_archive
101
+ )
102
+ }
103
+
104
+ for (method in methods) {
105
+ path <- tryCatch(method(version), error = identity)
106
+ if (is.character(path) && file.exists(path))
107
+ return(path)
108
+ }
109
+
110
+ stop("failed to download renv ", version)
111
+
112
+ }
113
+
114
+ renv_bootstrap_download_impl <- function(url, destfile) {
115
+
116
+ mode <- "wb"
117
+
118
+ # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715
119
+ fixup <-
120
+ Sys.info()[["sysname"]] == "Windows" &&
121
+ substring(url, 1L, 5L) == "file:"
122
+
123
+ if (fixup)
124
+ mode <- "w+b"
125
+
126
+ utils::download.file(
127
+ url = url,
128
+ destfile = destfile,
129
+ mode = mode,
130
+ quiet = TRUE
131
+ )
132
+
133
+ }
134
+
135
+ renv_bootstrap_download_cran_latest <- function(version) {
136
+
137
+ repos <- renv_bootstrap_download_cran_latest_find(version)
138
+
139
+ message("* Downloading renv ", version, " from CRAN ... ", appendLF = FALSE)
140
+
141
+ info <- tryCatch(
142
+ utils::download.packages(
143
+ pkgs = "renv",
144
+ repos = repos,
145
+ destdir = tempdir(),
146
+ quiet = TRUE
147
+ ),
148
+ condition = identity
149
+ )
150
+
151
+ if (inherits(info, "condition")) {
152
+ message("FAILED")
153
+ return(FALSE)
154
+ }
155
+
156
+ message("OK")
157
+ info[1, 2]
158
+
159
+ }
160
+
161
+ renv_bootstrap_download_cran_latest_find <- function(version) {
162
+
163
+ all <- renv_bootstrap_repos()
164
+
165
+ for (repos in all) {
166
+
167
+ db <- tryCatch(
168
+ as.data.frame(
169
+ x = utils::available.packages(repos = repos),
170
+ stringsAsFactors = FALSE
171
+ ),
172
+ error = identity
173
+ )
174
+
175
+ if (inherits(db, "error"))
176
+ next
177
+
178
+ entry <- db[db$Package %in% "renv" & db$Version %in% version, ]
179
+ if (nrow(entry) == 0)
180
+ next
181
+
182
+ return(repos)
183
+
184
+ }
185
+
186
+ fmt <- "renv %s is not available from your declared package repositories"
187
+ stop(sprintf(fmt, version))
188
+
189
+ }
190
+
191
+ renv_bootstrap_download_cran_archive <- function(version) {
192
+
193
+ name <- sprintf("renv_%s.tar.gz", version)
194
+ repos <- renv_bootstrap_repos()
195
+ urls <- file.path(repos, "src/contrib/Archive/renv", name)
196
+ destfile <- file.path(tempdir(), name)
197
+
198
+ message("* Downloading renv ", version, " from CRAN archive ... ", appendLF = FALSE)
199
+
200
+ for (url in urls) {
201
+
202
+ status <- tryCatch(
203
+ renv_bootstrap_download_impl(url, destfile),
204
+ condition = identity
205
+ )
206
+
207
+ if (identical(status, 0L)) {
208
+ message("OK")
209
+ return(destfile)
210
+ }
211
+
212
+ }
213
+
214
+ message("FAILED")
215
+ return(FALSE)
216
+
217
+ }
218
+
219
+ renv_bootstrap_download_github <- function(version) {
220
+
221
+ enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE")
222
+ if (!identical(enabled, "TRUE"))
223
+ return(FALSE)
224
+
225
+ # prepare download options
226
+ pat <- Sys.getenv("GITHUB_PAT")
227
+ if (nzchar(Sys.which("curl")) && nzchar(pat)) {
228
+ fmt <- "--location --fail --header \"Authorization: token %s\""
229
+ extra <- sprintf(fmt, pat)
230
+ saved <- options("download.file.method", "download.file.extra")
231
+ options(download.file.method = "curl", download.file.extra = extra)
232
+ on.exit(do.call(base::options, saved), add = TRUE)
233
+ } else if (nzchar(Sys.which("wget")) && nzchar(pat)) {
234
+ fmt <- "--header=\"Authorization: token %s\""
235
+ extra <- sprintf(fmt, pat)
236
+ saved <- options("download.file.method", "download.file.extra")
237
+ options(download.file.method = "wget", download.file.extra = extra)
238
+ on.exit(do.call(base::options, saved), add = TRUE)
239
+ }
240
+
241
+ message("* Downloading renv ", version, " from GitHub ... ", appendLF = FALSE)
242
+
243
+ url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version)
244
+ name <- sprintf("renv_%s.tar.gz", version)
245
+ destfile <- file.path(tempdir(), name)
246
+
247
+ status <- tryCatch(
248
+ renv_bootstrap_download_impl(url, destfile),
249
+ condition = identity
250
+ )
251
+
252
+ if (!identical(status, 0L)) {
253
+ message("FAILED")
254
+ return(FALSE)
255
+ }
256
+
257
+ message("OK")
258
+ return(destfile)
259
+
260
+ }
261
+
262
+ renv_bootstrap_install <- function(version, tarball, library) {
263
+
264
+ # attempt to install it into project library
265
+ message("* Installing renv ", version, " ... ", appendLF = FALSE)
266
+ dir.create(library, showWarnings = FALSE, recursive = TRUE)
267
+
268
+ # invoke using system2 so we can capture and report output
269
+ bin <- R.home("bin")
270
+ exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R"
271
+ r <- file.path(bin, exe)
272
+ args <- c("--vanilla", "CMD", "INSTALL", "-l", shQuote(library), shQuote(tarball))
273
+ output <- system2(r, args, stdout = TRUE, stderr = TRUE)
274
+ message("Done!")
275
+
276
+ # check for successful install
277
+ status <- attr(output, "status")
278
+ if (is.numeric(status) && !identical(status, 0L)) {
279
+ header <- "Error installing renv:"
280
+ lines <- paste(rep.int("=", nchar(header)), collapse = "")
281
+ text <- c(header, lines, output)
282
+ writeLines(text, con = stderr())
283
+ }
284
+
285
+ status
286
+
287
+ }
288
+
289
+ renv_bootstrap_prefix <- function() {
290
+
291
+ # construct version prefix
292
+ version <- paste(R.version$major, R.version$minor, sep = ".")
293
+ prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-")
294
+
295
+ # include SVN revision for development versions of R
296
+ # (to avoid sharing platform-specific artefacts with released versions of R)
297
+ devel <-
298
+ identical(R.version[["status"]], "Under development (unstable)") ||
299
+ identical(R.version[["nickname"]], "Unsuffered Consequences")
300
+
301
+ if (devel)
302
+ prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r")
303
+
304
+ # build list of path components
305
+ components <- c(prefix, R.version$platform)
306
+
307
+ # include prefix if provided by user
308
+ prefix <- Sys.getenv("RENV_PATHS_PREFIX")
309
+ if (nzchar(prefix))
310
+ components <- c(prefix, components)
311
+
312
+ # build prefix
313
+ paste(components, collapse = "/")
314
+
315
+ }
316
+
317
+ renv_bootstrap_library_root_name <- function(project) {
318
+
319
+ # use project name as-is if requested
320
+ asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE")
321
+ if (asis)
322
+ return(basename(project))
323
+
324
+ # otherwise, disambiguate based on project's path
325
+ id <- substring(renv_bootstrap_hash_text(project), 1L, 8L)
326
+ paste(basename(project), id, sep = "-")
327
+
328
+ }
329
+
330
+ renv_bootstrap_library_root <- function(project) {
331
+
332
+ path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA)
333
+ if (!is.na(path))
334
+ return(path)
335
+
336
+ path <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA)
337
+ if (!is.na(path)) {
338
+ name <- renv_bootstrap_library_root_name(project)
339
+ return(file.path(path, name))
340
+ }
341
+
342
+ file.path(project, "renv/library")
343
+
344
+ }
345
+
346
+ renv_bootstrap_validate_version <- function(version) {
347
+
348
+ loadedversion <- utils::packageDescription("renv", fields = "Version")
349
+ if (version == loadedversion)
350
+ return(TRUE)
351
+
352
+ # assume four-component versions are from GitHub; three-component
353
+ # versions are from CRAN
354
+ components <- strsplit(loadedversion, "[.-]")[[1]]
355
+ remote <- if (length(components) == 4L)
356
+ paste("rstudio/renv", loadedversion, sep = "@")
357
+ else
358
+ paste("renv", loadedversion, sep = "@")
359
+
360
+ fmt <- paste(
361
+ "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.",
362
+ "Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.",
363
+ "Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.",
364
+ sep = "\n"
365
+ )
366
+
367
+ msg <- sprintf(fmt, loadedversion, version, remote)
368
+ warning(msg, call. = FALSE)
369
+
370
+ FALSE
371
+
372
+ }
373
+
374
+ renv_bootstrap_hash_text <- function(text) {
375
+
376
+ hashfile <- tempfile("renv-hash-")
377
+ on.exit(unlink(hashfile), add = TRUE)
378
+
379
+ writeLines(text, con = hashfile)
380
+ tools::md5sum(hashfile)
381
+
382
+ }
383
+
384
+ renv_bootstrap_load <- function(project, libpath, version) {
385
+
386
+ # try to load renv from the project library
387
+ if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE))
388
+ return(FALSE)
389
+
390
+ # warn if the version of renv loaded does not match
391
+ renv_bootstrap_validate_version(version)
392
+
393
+ # load the project
394
+ renv::load(project)
395
+
396
+ TRUE
397
+
398
+ }
399
+
400
+ # construct path to library root
401
+ root <- renv_bootstrap_library_root(project)
402
+
403
+ # construct library prefix for platform
404
+ prefix <- renv_bootstrap_prefix()
405
+
406
+ # construct full libpath
407
+ libpath <- file.path(root, prefix)
408
+
409
+ # attempt to load
410
+ if (renv_bootstrap_load(project, libpath, version))
411
+ return(TRUE)
412
+
413
+ # load failed; inform user we're about to bootstrap
414
+ prefix <- paste("# Bootstrapping renv", version)
415
+ postfix <- paste(rep.int("-", 77L - nchar(prefix)), collapse = "")
416
+ header <- paste(prefix, postfix)
417
+ message(header)
418
+
419
+ # perform bootstrap
420
+ bootstrap(version, libpath)
421
+
422
+ # exit early if we're just testing bootstrap
423
+ if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA)))
424
+ return(TRUE)
425
+
426
+ # try again to load
427
+ if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) {
428
+ message("* Successfully installed and loaded renv ", version, ".")
429
+ return(renv::load())
430
+ }
431
+
432
+ # failed to download or load renv; warn the user
433
+ msg <- c(
434
+ "Failed to find an renv installation: the project will not be loaded.",
435
+ "Use `renv::activate()` to re-initialize the project."
436
+ )
437
+
438
+ warning(paste(msg, collapse = "\n"), call. = FALSE)
439
+
440
+ })
@@ -0,0 +1,9 @@
1
+ channels:
2
+ - conda-forge
3
+ - defaults
4
+ dependencies:
5
+ # This cannot be empty as otherwise no environment will be created.
6
+ # We're using openssl here as it is available on all system and will
7
+ # most likely be always installed anyways.
8
+ # See https://github.com/conda/conda/issues/9487
9
+ - openssl
@@ -0,0 +1 @@
1
+ module pre-commit-placeholder-empty-module
@@ -0,0 +1,3 @@
1
+ package main
2
+
3
+ func main() {}
@@ -0,0 +1 @@
1
+ fn main() {}
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "pre_commit_placeholder_package",
3
+ "version": "0.0.0"
4
+ }
@@ -0,0 +1,12 @@
1
+ package = "pre-commit-package"
2
+ version = "dev-1"
3
+
4
+ source = {
5
+ url = "git+ssh://git@github.com/pre-commit/pre-commit.git"
6
+ }
7
+ description = {}
8
+ dependencies = {}
9
+ build = {
10
+ type = "builtin",
11
+ modules = {},
12
+ }
@@ -0,0 +1,6 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'pre_commit_placeholder_package'
3
+ s.version = '0.0.0'
4
+ s.summary = 'placeholder gem for pre-commit hooks'
5
+ s.authors = ['Anthony Sottile']
6
+ end
@@ -0,0 +1,4 @@
1
+ name: pre_commit_empty_pubspec
2
+ environment:
3
+ sdk: '>=2.12.0'
4
+ executables: {}
@@ -0,0 +1,20 @@
1
+ {
2
+ "R": {
3
+ "Version": "4.0.3",
4
+ "Repositories": [
5
+ {
6
+ "Name": "CRAN",
7
+ "URL": "https://cran.rstudio.com"
8
+ }
9
+ ]
10
+ },
11
+ "Packages": {
12
+ "renv": {
13
+ "Package": "renv",
14
+ "Version": "0.12.5",
15
+ "Source": "Repository",
16
+ "Repository": "CRAN",
17
+ "Hash": "5c0cdb37f063c58cdab3c7e9fbb8bd2c"
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,4 @@
1
+ from setuptools import setup
2
+
3
+
4
+ setup(name='pre-commit-placeholder-package', version='0.0.0', py_modules=[])
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # File generated by pre-commit: https://pre-commit.com
3
+ # ID: 138fd403232d2ddd5efb44317e38bf03
4
+
5
+ # start templated
6
+ INSTALL_PYTHON=''
7
+ ARGS=(hook-impl)
8
+ # end templated
9
+
10
+ HERE="$(cd "$(dirname "$0")" && pwd)"
11
+ ARGS+=(--hook-dir "$HERE" -- "$@")
12
+
13
+ if [ -x "$INSTALL_PYTHON" ]; then
14
+ exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}"
15
+ elif command -v pre-commit > /dev/null; then
16
+ exec pre-commit "${ARGS[@]}"
17
+ else
18
+ echo '`pre-commit` not found. Did you forget to activate your virtualenv?' 1>&2
19
+ exit 1
20
+ fi
Binary file
Binary file
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import logging
5
+ import os.path
6
+ import time
7
+ from collections.abc import Generator
8
+
9
+ from pre_commit import git
10
+ from pre_commit.errors import FatalError
11
+ from pre_commit.util import CalledProcessError
12
+ from pre_commit.util import cmd_output
13
+ from pre_commit.util import cmd_output_b
14
+ from pre_commit.xargs import xargs
15
+
16
+
17
+ logger = logging.getLogger('pre_commit')
18
+
19
+ # without forcing submodule.recurse=0, changes in nested submodules will be
20
+ # discarded if `submodule.recurse=1` is configured
21
+ # we choose this instead of `--no-recurse-submodules` because it works on
22
+ # versions of git before that option was added to `git checkout`
23
+ _CHECKOUT_CMD = ('git', '-c', 'submodule.recurse=0', 'checkout', '--', '.')
24
+
25
+
26
+ def _git_apply(patch: str) -> None:
27
+ args = ('apply', '--whitespace=nowarn', patch)
28
+ try:
29
+ cmd_output_b('git', *args)
30
+ except CalledProcessError:
31
+ # Retry with autocrlf=false -- see #570
32
+ cmd_output_b('git', '-c', 'core.autocrlf=false', *args)
33
+
34
+
35
+ @contextlib.contextmanager
36
+ def _intent_to_add_cleared() -> Generator[None]:
37
+ intent_to_add = git.intent_to_add_files()
38
+ if intent_to_add:
39
+ logger.warning('Unstaged intent-to-add files detected.')
40
+
41
+ xargs(('git', 'rm', '--cached', '--'), intent_to_add)
42
+ try:
43
+ yield
44
+ finally:
45
+ xargs(('git', 'add', '--intent-to-add', '--'), intent_to_add)
46
+ else:
47
+ yield
48
+
49
+
50
+ @contextlib.contextmanager
51
+ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None]:
52
+ tree = cmd_output('git', 'write-tree')[1].strip()
53
+ diff_cmd = (
54
+ 'git', 'diff-index', '--ignore-submodules', '--binary',
55
+ '--exit-code', '--no-color', '--no-ext-diff', tree, '--',
56
+ )
57
+ retcode, diff_stdout, diff_stderr = cmd_output_b(*diff_cmd, check=False)
58
+ if retcode == 0:
59
+ # There weren't any staged files so we don't need to do anything
60
+ # special
61
+ yield
62
+ elif retcode == 1 and not diff_stdout.strip():
63
+ # due to behaviour (probably a bug?) in git with crlf endings and
64
+ # autocrlf set to either `true` or `input` sometimes git will refuse
65
+ # to show a crlf-only diff to us :(
66
+ yield
67
+ elif retcode == 1 and diff_stdout.strip():
68
+ patch_filename = f'patch{int(time.time())}-{os.getpid()}'
69
+ patch_filename = os.path.join(patch_dir, patch_filename)
70
+ logger.warning('Unstaged files detected.')
71
+ logger.info(f'Stashing unstaged files to {patch_filename}.')
72
+ # Save the current unstaged changes as a patch
73
+ os.makedirs(patch_dir, exist_ok=True)
74
+ with open(patch_filename, 'wb') as patch_file:
75
+ patch_file.write(diff_stdout)
76
+
77
+ # prevent recursive post-checkout hooks (#1418)
78
+ no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')
79
+
80
+ try:
81
+ cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
82
+ yield
83
+ finally:
84
+ # Try to apply the patch we saved
85
+ try:
86
+ _git_apply(patch_filename)
87
+ except CalledProcessError:
88
+ logger.warning(
89
+ 'Stashed changes conflicted with hook auto-fixes... '
90
+ 'Rolling back fixes...',
91
+ )
92
+ # We failed to apply the patch, presumably due to fixes made
93
+ # by hooks.
94
+ # Roll back the changes made by hooks.
95
+ cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
96
+ _git_apply(patch_filename)
97
+
98
+ logger.info(f'Restored changes from {patch_filename}.')
99
+ else: # pragma: win32 no cover
100
+ # some error occurred while requesting the diff
101
+ e = CalledProcessError(retcode, diff_cmd, b'', diff_stderr)
102
+ raise FatalError(
103
+ f'pre-commit failed to diff -- perhaps due to permissions?\n\n{e}',
104
+ )
105
+
106
+
107
+ @contextlib.contextmanager
108
+ def staged_files_only(patch_dir: str) -> Generator[None]:
109
+ """Clear any unstaged changes from the git working directory inside this
110
+ context.
111
+ """
112
+ with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):
113
+ yield