]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_dev/src/setup/git_hook.rs
Rollup merge of #86439 - CDirkx:ip-protocol-assignment, r=m-ou-se
[rust.git] / src / tools / clippy / clippy_dev / src / setup / git_hook.rs
1 use std::fs;
2 use std::path::Path;
3
4 use super::verify_inside_clippy_dir;
5
6 /// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo.
7 /// I've decided against this for the sake of simplicity and to make sure that it doesn't install
8 /// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool
9 /// for formatting and should therefor only be used in a normal clone of clippy
10 const REPO_GIT_DIR: &str = ".git";
11 const HOOK_SOURCE_FILE: &str = "util/etc/pre-commit.sh";
12 const HOOK_TARGET_FILE: &str = ".git/hooks/pre-commit";
13
14 pub fn install_hook(force_override: bool) {
15     if !check_precondition(force_override) {
16         return;
17     }
18
19     // So a little bit of a funny story. Git on unix requires the pre-commit file
20     // to have the `execute` permission to be set. The Rust functions for modifying
21     // these flags doesn't seem to work when executed with normal user permissions.
22     //
23     // However, there is a little hack that is also being used by Rust itself in their
24     // setup script. Git saves the `execute` flag when syncing files. This means
25     // that we can check in a file with execution permissions and the sync it to create
26     // a file with the flag set. We then copy this file here. The copy function will also
27     // include the `execute` permission.
28     match fs::copy(HOOK_SOURCE_FILE, HOOK_TARGET_FILE) {
29         Ok(_) => {
30             println!("info: the hook can be removed with `cargo dev remove git-hook`");
31             println!("git hook successfully installed");
32         },
33         Err(err) => eprintln!(
34             "error: unable to copy `{}` to `{}` ({})",
35             HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err
36         ),
37     }
38 }
39
40 fn check_precondition(force_override: bool) -> bool {
41     if !verify_inside_clippy_dir() {
42         return false;
43     }
44
45     // Make sure that we can find the git repository
46     let git_path = Path::new(REPO_GIT_DIR);
47     if !git_path.exists() || !git_path.is_dir() {
48         eprintln!("error: clippy_dev was unable to find the `.git` directory");
49         return false;
50     }
51
52     // Make sure that we don't override an existing hook by accident
53     let path = Path::new(HOOK_TARGET_FILE);
54     if path.exists() {
55         if force_override {
56             return delete_git_hook_file(path);
57         }
58
59         eprintln!("error: there is already a pre-commit hook installed");
60         println!("info: use the `--force-override` flag to override the existing hook");
61         return false;
62     }
63
64     true
65 }
66
67 pub fn remove_hook() {
68     let path = Path::new(HOOK_TARGET_FILE);
69     if path.exists() {
70         if delete_git_hook_file(path) {
71             println!("git hook successfully removed");
72         }
73     } else {
74         println!("no pre-commit hook was found");
75     }
76 }
77
78 fn delete_git_hook_file(path: &Path) -> bool {
79     if let Err(err) = fs::remove_file(path) {
80         eprintln!("error: unable to delete existing pre-commit git hook ({})", err);
81         false
82     } else {
83         true
84     }
85 }