/src/git/builtin/merge-base.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "builtin.h" |
2 | | #include "config.h" |
3 | | #include "commit.h" |
4 | | #include "gettext.h" |
5 | | #include "hex.h" |
6 | | #include "object-name.h" |
7 | | #include "parse-options.h" |
8 | | #include "repository.h" |
9 | | #include "commit-reach.h" |
10 | | |
11 | | static int show_merge_base(struct commit **rev, int rev_nr, int show_all) |
12 | 0 | { |
13 | 0 | struct commit_list *result = NULL, *r; |
14 | |
|
15 | 0 | if (repo_get_merge_bases_many_dirty(the_repository, rev[0], |
16 | 0 | rev_nr - 1, rev + 1, &result) < 0) { |
17 | 0 | free_commit_list(result); |
18 | 0 | return -1; |
19 | 0 | } |
20 | | |
21 | 0 | if (!result) |
22 | 0 | return 1; |
23 | | |
24 | 0 | for (r = result; r; r = r->next) { |
25 | 0 | printf("%s\n", oid_to_hex(&r->item->object.oid)); |
26 | 0 | if (!show_all) |
27 | 0 | break; |
28 | 0 | } |
29 | |
|
30 | 0 | free_commit_list(result); |
31 | 0 | return 0; |
32 | 0 | } |
33 | | |
34 | | static const char * const merge_base_usage[] = { |
35 | | N_("git merge-base [-a | --all] <commit> <commit>..."), |
36 | | N_("git merge-base [-a | --all] --octopus <commit>..."), |
37 | | N_("git merge-base --is-ancestor <commit> <commit>"), |
38 | | N_("git merge-base --independent <commit>..."), |
39 | | N_("git merge-base --fork-point <ref> [<commit>]"), |
40 | | NULL |
41 | | }; |
42 | | |
43 | | static struct commit *get_commit_reference(const char *arg) |
44 | 0 | { |
45 | 0 | struct object_id revkey; |
46 | 0 | struct commit *r; |
47 | |
|
48 | 0 | if (repo_get_oid(the_repository, arg, &revkey)) |
49 | 0 | die("Not a valid object name %s", arg); |
50 | 0 | r = lookup_commit_reference(the_repository, &revkey); |
51 | 0 | if (!r) |
52 | 0 | die("Not a valid commit name %s", arg); |
53 | | |
54 | 0 | return r; |
55 | 0 | } |
56 | | |
57 | | static int handle_independent(int count, const char **args) |
58 | 0 | { |
59 | 0 | struct commit_list *revs = NULL, *rev; |
60 | 0 | int i; |
61 | |
|
62 | 0 | for (i = count - 1; i >= 0; i--) |
63 | 0 | commit_list_insert(get_commit_reference(args[i]), &revs); |
64 | |
|
65 | 0 | reduce_heads_replace(&revs); |
66 | |
|
67 | 0 | if (!revs) |
68 | 0 | return 1; |
69 | | |
70 | 0 | for (rev = revs; rev; rev = rev->next) |
71 | 0 | printf("%s\n", oid_to_hex(&rev->item->object.oid)); |
72 | |
|
73 | 0 | free_commit_list(revs); |
74 | 0 | return 0; |
75 | 0 | } |
76 | | |
77 | | static int handle_octopus(int count, const char **args, int show_all) |
78 | 0 | { |
79 | 0 | struct commit_list *revs = NULL; |
80 | 0 | struct commit_list *result = NULL, *rev; |
81 | 0 | int i; |
82 | |
|
83 | 0 | for (i = count - 1; i >= 0; i--) |
84 | 0 | commit_list_insert(get_commit_reference(args[i]), &revs); |
85 | |
|
86 | 0 | if (get_octopus_merge_bases(revs, &result) < 0) { |
87 | 0 | free_commit_list(revs); |
88 | 0 | free_commit_list(result); |
89 | 0 | return 128; |
90 | 0 | } |
91 | 0 | free_commit_list(revs); |
92 | 0 | reduce_heads_replace(&result); |
93 | |
|
94 | 0 | if (!result) |
95 | 0 | return 1; |
96 | | |
97 | 0 | for (rev = result; rev; rev = rev->next) { |
98 | 0 | printf("%s\n", oid_to_hex(&rev->item->object.oid)); |
99 | 0 | if (!show_all) |
100 | 0 | break; |
101 | 0 | } |
102 | |
|
103 | 0 | free_commit_list(result); |
104 | 0 | return 0; |
105 | 0 | } |
106 | | |
107 | | static int handle_is_ancestor(int argc, const char **argv) |
108 | 0 | { |
109 | 0 | struct commit *one, *two; |
110 | 0 | int ret; |
111 | |
|
112 | 0 | if (argc != 2) |
113 | 0 | die("--is-ancestor takes exactly two commits"); |
114 | 0 | one = get_commit_reference(argv[0]); |
115 | 0 | two = get_commit_reference(argv[1]); |
116 | 0 | ret = repo_in_merge_bases(the_repository, one, two); |
117 | 0 | if (ret < 0) |
118 | 0 | exit(128); |
119 | 0 | if (ret) |
120 | 0 | return 0; |
121 | 0 | else |
122 | 0 | return 1; |
123 | 0 | } |
124 | | |
125 | | static int handle_fork_point(int argc, const char **argv) |
126 | 0 | { |
127 | 0 | struct object_id oid; |
128 | 0 | struct commit *derived, *fork_point; |
129 | 0 | const char *commitname; |
130 | |
|
131 | 0 | commitname = (argc == 2) ? argv[1] : "HEAD"; |
132 | 0 | if (repo_get_oid(the_repository, commitname, &oid)) |
133 | 0 | die("Not a valid object name: '%s'", commitname); |
134 | | |
135 | 0 | derived = lookup_commit_reference(the_repository, &oid); |
136 | |
|
137 | 0 | fork_point = get_fork_point(argv[0], derived); |
138 | |
|
139 | 0 | if (!fork_point) |
140 | 0 | return 1; |
141 | | |
142 | 0 | printf("%s\n", oid_to_hex(&fork_point->object.oid)); |
143 | 0 | return 0; |
144 | 0 | } |
145 | | |
146 | | int cmd_merge_base(int argc, const char **argv, const char *prefix) |
147 | 0 | { |
148 | 0 | struct commit **rev; |
149 | 0 | int rev_nr = 0; |
150 | 0 | int show_all = 0; |
151 | 0 | int cmdmode = 0; |
152 | 0 | int ret; |
153 | |
|
154 | 0 | struct option options[] = { |
155 | 0 | OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")), |
156 | 0 | OPT_CMDMODE(0, "octopus", &cmdmode, |
157 | 0 | N_("find ancestors for a single n-way merge"), 'o'), |
158 | 0 | OPT_CMDMODE(0, "independent", &cmdmode, |
159 | 0 | N_("list revs not reachable from others"), 'r'), |
160 | 0 | OPT_CMDMODE(0, "is-ancestor", &cmdmode, |
161 | 0 | N_("is the first one ancestor of the other?"), 'a'), |
162 | 0 | OPT_CMDMODE(0, "fork-point", &cmdmode, |
163 | 0 | N_("find where <commit> forked from reflog of <ref>"), 'f'), |
164 | 0 | OPT_END() |
165 | 0 | }; |
166 | |
|
167 | 0 | git_config(git_default_config, NULL); |
168 | 0 | argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); |
169 | |
|
170 | 0 | if (cmdmode == 'a') { |
171 | 0 | if (argc < 2) |
172 | 0 | usage_with_options(merge_base_usage, options); |
173 | 0 | if (show_all) |
174 | 0 | die(_("options '%s' and '%s' cannot be used together"), |
175 | 0 | "--is-ancestor", "--all"); |
176 | 0 | return handle_is_ancestor(argc, argv); |
177 | 0 | } |
178 | | |
179 | 0 | if (cmdmode == 'r' && show_all) |
180 | 0 | die(_("options '%s' and '%s' cannot be used together"), |
181 | 0 | "--independent", "--all"); |
182 | | |
183 | 0 | if (cmdmode == 'o') |
184 | 0 | return handle_octopus(argc, argv, show_all); |
185 | | |
186 | 0 | if (cmdmode == 'r') |
187 | 0 | return handle_independent(argc, argv); |
188 | | |
189 | 0 | if (cmdmode == 'f') { |
190 | 0 | if (argc < 1 || 2 < argc) |
191 | 0 | usage_with_options(merge_base_usage, options); |
192 | 0 | return handle_fork_point(argc, argv); |
193 | 0 | } |
194 | | |
195 | 0 | if (argc < 2) |
196 | 0 | usage_with_options(merge_base_usage, options); |
197 | | |
198 | 0 | ALLOC_ARRAY(rev, argc); |
199 | 0 | while (argc-- > 0) |
200 | 0 | rev[rev_nr++] = get_commit_reference(*argv++); |
201 | 0 | ret = show_merge_base(rev, rev_nr, show_all); |
202 | 0 | free(rev); |
203 | 0 | return ret; |
204 | 0 | } |