]>
Commit | Line | Data |
---|---|---|
1bf072e3 CC |
1 | #include "builtin.h" |
2 | #include "cache.h" | |
3 | #include "parse-options.h" | |
4 | #include "bisect.h" | |
4ba1e5c4 | 5 | #include "refs.h" |
1bf072e3 | 6 | |
ecb3f373 | 7 | static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") |
b903674b PB |
8 | static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") |
9 | static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") | |
ecb3f373 | 10 | |
1bf072e3 | 11 | static const char * const git_bisect_helper_usage[] = { |
9e7bbe2d | 12 | N_("git bisect--helper --next-all [--no-checkout]"), |
ecb3f373 | 13 | N_("git bisect--helper --write-terms <bad_term> <good_term>"), |
fb71a329 | 14 | N_("git bisect--helper --bisect-clean-state"), |
1bf072e3 CC |
15 | NULL |
16 | }; | |
17 | ||
4ba1e5c4 PB |
18 | /* |
19 | * Check whether the string `term` belongs to the set of strings | |
20 | * included in the variable arguments. | |
21 | */ | |
22 | LAST_ARG_MUST_BE_NULL | |
23 | static int one_of(const char *term, ...) | |
24 | { | |
25 | int res = 0; | |
26 | va_list matches; | |
27 | const char *match; | |
28 | ||
29 | va_start(matches, term); | |
30 | while (!res && (match = va_arg(matches, const char *))) | |
31 | res = !strcmp(term, match); | |
32 | va_end(matches); | |
33 | ||
34 | return res; | |
35 | } | |
36 | ||
37 | static int check_term_format(const char *term, const char *orig_term) | |
38 | { | |
39 | int res; | |
40 | char *new_term = xstrfmt("refs/bisect/%s", term); | |
41 | ||
42 | res = check_refname_format(new_term, 0); | |
43 | free(new_term); | |
44 | ||
45 | if (res) | |
46 | return error(_("'%s' is not a valid term"), term); | |
47 | ||
48 | if (one_of(term, "help", "start", "skip", "next", "reset", | |
dbc349bb | 49 | "visualize", "view", "replay", "log", "run", "terms", NULL)) |
4ba1e5c4 PB |
50 | return error(_("can't use the builtin command '%s' as a term"), term); |
51 | ||
52 | /* | |
53 | * In theory, nothing prevents swapping completely good and bad, | |
54 | * but this situation could be confusing and hasn't been tested | |
55 | * enough. Forbid it for now. | |
56 | */ | |
57 | ||
58 | if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) || | |
59 | (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL))) | |
60 | return error(_("can't change the meaning of the term '%s'"), term); | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
ecb3f373 PB |
65 | static int write_terms(const char *bad, const char *good) |
66 | { | |
67 | FILE *fp = NULL; | |
68 | int res; | |
69 | ||
70 | if (!strcmp(bad, good)) | |
71 | return error(_("please use two different terms")); | |
72 | ||
73 | if (check_term_format(bad, "bad") || check_term_format(good, "good")) | |
74 | return -1; | |
75 | ||
76 | fp = fopen(git_path_bisect_terms(), "w"); | |
77 | if (!fp) | |
78 | return error_errno(_("could not open the file BISECT_TERMS")); | |
79 | ||
80 | res = fprintf(fp, "%s\n%s\n", bad, good); | |
81 | res |= fclose(fp); | |
82 | return (res < 0) ? -1 : 0; | |
83 | } | |
84 | ||
b903674b PB |
85 | static int is_expected_rev(const char *expected_hex) |
86 | { | |
87 | struct strbuf actual_hex = STRBUF_INIT; | |
88 | int res = 0; | |
89 | if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) { | |
90 | strbuf_trim(&actual_hex); | |
91 | res = !strcmp(actual_hex.buf, expected_hex); | |
92 | } | |
93 | strbuf_release(&actual_hex); | |
94 | return res; | |
95 | } | |
96 | ||
97 | static void check_expected_revs(const char **revs, int rev_nr) | |
98 | { | |
99 | int i; | |
100 | ||
101 | for (i = 0; i < rev_nr; i++) { | |
102 | if (!is_expected_rev(revs[i])) { | |
103 | unlink_or_warn(git_path_bisect_ancestors_ok()); | |
104 | unlink_or_warn(git_path_bisect_expected_rev()); | |
105 | } | |
106 | } | |
107 | } | |
108 | ||
1bf072e3 CC |
109 | int cmd_bisect__helper(int argc, const char **argv, const char *prefix) |
110 | { | |
4ba1e5c4 PB |
111 | enum { |
112 | NEXT_ALL = 1, | |
fb71a329 | 113 | WRITE_TERMS, |
b903674b PB |
114 | BISECT_CLEAN_STATE, |
115 | CHECK_EXPECTED_REVS | |
4ba1e5c4 | 116 | } cmdmode = 0; |
fee92fc1 | 117 | int no_checkout = 0; |
1bf072e3 | 118 | struct option options[] = { |
9e1c84df PB |
119 | OPT_CMDMODE(0, "next-all", &cmdmode, |
120 | N_("perform 'git bisect next'"), NEXT_ALL), | |
ecb3f373 PB |
121 | OPT_CMDMODE(0, "write-terms", &cmdmode, |
122 | N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS), | |
fb71a329 PB |
123 | OPT_CMDMODE(0, "bisect-clean-state", &cmdmode, |
124 | N_("cleanup the bisection state"), BISECT_CLEAN_STATE), | |
b903674b PB |
125 | OPT_CMDMODE(0, "check-expected-revs", &cmdmode, |
126 | N_("check for expected revs"), CHECK_EXPECTED_REVS), | |
d5d09d47 SB |
127 | OPT_BOOL(0, "no-checkout", &no_checkout, |
128 | N_("update BISECT_HEAD instead of checking out the current commit")), | |
1bf072e3 CC |
129 | OPT_END() |
130 | }; | |
131 | ||
37782920 SB |
132 | argc = parse_options(argc, argv, prefix, options, |
133 | git_bisect_helper_usage, 0); | |
1bf072e3 | 134 | |
9e1c84df | 135 | if (!cmdmode) |
1bf072e3 CC |
136 | usage_with_options(git_bisect_helper_usage, options); |
137 | ||
9e1c84df PB |
138 | switch (cmdmode) { |
139 | case NEXT_ALL: | |
140 | return bisect_next_all(prefix, no_checkout); | |
ecb3f373 | 141 | case WRITE_TERMS: |
4ba1e5c4 | 142 | if (argc != 2) |
ecb3f373 PB |
143 | return error(_("--write-terms requires two arguments")); |
144 | return write_terms(argv[0], argv[1]); | |
fb71a329 PB |
145 | case BISECT_CLEAN_STATE: |
146 | if (argc != 0) | |
147 | return error(_("--bisect-clean-state requires no arguments")); | |
148 | return bisect_clean_state(); | |
b903674b PB |
149 | case CHECK_EXPECTED_REVS: |
150 | check_expected_revs(argv, argc); | |
151 | return 0; | |
9e1c84df PB |
152 | default: |
153 | return error("BUG: unknown subcommand '%d'", cmdmode); | |
154 | } | |
155 | return 0; | |
1bf072e3 | 156 | } |