1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This file contains functions for git repos.
7 */
8 module autoformat.git;
9 
10 import std.exception;
11 import std.file;
12 import std.path;
13 import std.process;
14 
15 import my.optional;
16 
17 import autoformat.types;
18 
19 bool isGitRoot(AbsolutePath arg_p) nothrow {
20     if (!exists(arg_p)) {
21         return false;
22     } else if (exists(buildPath(arg_p, ".git", "refs"))) {
23         // all is well! a real git repo found
24         return true;
25     } else if (exists(buildPath(arg_p, "refs"))) {
26         // humpf, submodule
27         return true;
28     }
29 
30     return false;
31 }
32 
33 /// If the current working directory is a git repo
34 bool isGitRepo() nothrow {
35     try {
36         auto r = execute(["git", "rev-parse", "--show-toplevel"]);
37         return r.status == 0;
38     } catch (Exception ex) {
39     }
40 
41     return false;
42 }
43 
44 Optional!AbsolutePath gitHookPath(AbsolutePath repo) {
45     immutable regular = buildPath(repo, ".git", "hooks");
46     immutable submodule = buildPath(repo, "hooks");
47 
48     if (exists(regular)) {
49         return some(AbsolutePath(regular));
50     } else if (exists(buildPath(repo, "refs"))) {
51         return some(AbsolutePath(submodule));
52     }
53 
54     return none!AbsolutePath;
55 }
56 
57 /// Path to the root of the git archive from current.
58 Optional!AbsolutePath gitPathToRoot() @safe {
59     import std.string : strip;
60 
61     try {
62         auto r = execute(["git", "rev-parse", "--show-toplevel"]);
63         return some(AbsolutePath(r.output.strip));
64     } catch (Exception ex) {
65     }
66 
67     return none!AbsolutePath;
68 }
69 
70 /** Resolves the path to the git directory.
71  * Not necessarily the same as the root.
72  * Useful for finding the root of a hierarchy of submodules.
73  */
74 Optional!AbsolutePath gitPathToTrueRoot() nothrow {
75     import std.string : strip;
76 
77     try {
78         auto p = buildPath(gitPathToRoot.toString, ".git");
79         auto r = execute(["git", "rev-parse", "--resolve-git-dir", p]);
80         return some(AbsolutePath(r.output.strip));
81     } catch (Exception ex) {
82     }
83 
84     return none!AbsolutePath;
85 }
86 
87 Optional!string gitConfigValue(string c) {
88     import std.string : strip;
89 
90     try {
91         auto a = execute(["git", "config", c]);
92         if (a.status != 0) {
93             return none!string;
94         }
95 
96         return some(a.output.strip);
97     } catch (ErrnoException ex) {
98     }
99 
100     return none!string;
101 }
102 
103 struct GitHash {
104     string value;
105     alias value this;
106 }
107 
108 GitHash gitHead() nothrow {
109     import std.string : strip;
110 
111     // Initial commit: diff against an empty tree object
112     auto h = GitHash("4b825dc642cb6eb9a060e54bf8d69288fbee4904");
113 
114     try {
115         auto res = execute(["git", "rev-parse", "--verify", "HEAD"]);
116         if (res.status == 0) {
117             h = GitHash(res.output.strip);
118         }
119     } catch (Exception ex) {
120     }
121 
122     return h;
123 }