1
1
#include "builtin.h"
2
2
#include "cache.h"
3
3
#include "commit.h"
4
+ #include "refs.h"
5
+ #include "diff.h"
6
+ #include "revision.h"
4
7
#include "parse-options.h"
5
8
6
9
static int show_merge_base (struct commit * * rev , int rev_nr , int show_all )
@@ -27,6 +30,7 @@ static const char * const merge_base_usage[] = {
27
30
N_ ("git merge-base [-a|--all] --octopus <commit>..." ),
28
31
N_ ("git merge-base --independent <commit>..." ),
29
32
N_ ("git merge-base --is-ancestor <commit> <commit>" ),
33
+ N_ ("git merge-base --fork-point <ref> [<commit>]" ),
30
34
NULL
31
35
};
32
36
@@ -85,37 +89,148 @@ static int handle_is_ancestor(int argc, const char **argv)
85
89
return 1 ;
86
90
}
87
91
92
+ struct rev_collect {
93
+ struct commit * * commit ;
94
+ int nr ;
95
+ int alloc ;
96
+ unsigned int initial : 1 ;
97
+ };
98
+
99
+ static void add_one_commit (unsigned char * sha1 , struct rev_collect * revs )
100
+ {
101
+ struct commit * commit ;
102
+
103
+ if (is_null_sha1 (sha1 ))
104
+ return ;
105
+
106
+ commit = lookup_commit (sha1 );
107
+ if (!commit ||
108
+ (commit -> object .flags & TMP_MARK ) ||
109
+ parse_commit (commit ))
110
+ return ;
111
+
112
+ ALLOC_GROW (revs -> commit , revs -> nr + 1 , revs -> alloc );
113
+ revs -> commit [revs -> nr ++ ] = commit ;
114
+ commit -> object .flags |= TMP_MARK ;
115
+ }
116
+
117
+ static int collect_one_reflog_ent (unsigned char * osha1 , unsigned char * nsha1 ,
118
+ const char * ident , unsigned long timestamp ,
119
+ int tz , const char * message , void * cbdata )
120
+ {
121
+ struct rev_collect * revs = cbdata ;
122
+
123
+ if (revs -> initial ) {
124
+ revs -> initial = 0 ;
125
+ add_one_commit (osha1 , revs );
126
+ }
127
+ add_one_commit (nsha1 , revs );
128
+ return 0 ;
129
+ }
130
+
131
+ static int handle_fork_point (int argc , const char * * argv )
132
+ {
133
+ unsigned char sha1 [20 ];
134
+ char * refname ;
135
+ const char * commitname ;
136
+ struct rev_collect revs ;
137
+ struct commit * derived ;
138
+ struct commit_list * bases ;
139
+ int i , ret = 0 ;
140
+
141
+ switch (dwim_ref (argv [0 ], strlen (argv [0 ]), sha1 , & refname )) {
142
+ case 0 :
143
+ die ("No such ref: '%s'" , argv [0 ]);
144
+ case 1 :
145
+ break ; /* good */
146
+ default :
147
+ die ("Ambiguous refname: '%s'" , argv [0 ]);
148
+ }
149
+
150
+ commitname = (argc == 2 ) ? argv [1 ] : "HEAD" ;
151
+ if (get_sha1 (commitname , sha1 ))
152
+ die ("Not a valid object name: '%s'" , commitname );
153
+
154
+ derived = lookup_commit_reference (sha1 );
155
+ memset (& revs , 0 , sizeof (revs ));
156
+ revs .initial = 1 ;
157
+ for_each_reflog_ent (refname , collect_one_reflog_ent , & revs );
158
+
159
+ for (i = 0 ; i < revs .nr ; i ++ )
160
+ revs .commit [i ]-> object .flags &= ~TMP_MARK ;
161
+
162
+ bases = get_merge_bases_many (derived , revs .nr , revs .commit , 0 );
163
+
164
+ /*
165
+ * There should be one and only one merge base, when we found
166
+ * a common ancestor among reflog entries.
167
+ */
168
+ if (!bases || bases -> next ) {
169
+ ret = 1 ;
170
+ goto cleanup_return ;
171
+ }
172
+
173
+ /* And the found one must be one of the reflog entries */
174
+ for (i = 0 ; i < revs .nr ; i ++ )
175
+ if (& bases -> item -> object == & revs .commit [i ]-> object )
176
+ break ; /* found */
177
+ if (revs .nr <= i ) {
178
+ ret = 1 ; /* not found */
179
+ goto cleanup_return ;
180
+ }
181
+
182
+ printf ("%s\n" , sha1_to_hex (bases -> item -> object .sha1 ));
183
+
184
+ cleanup_return :
185
+ free_commit_list (bases );
186
+ return ret ;
187
+ }
188
+
88
189
int cmd_merge_base (int argc , const char * * argv , const char * prefix )
89
190
{
90
191
struct commit * * rev ;
91
192
int rev_nr = 0 ;
92
193
int show_all = 0 ;
93
- int octopus = 0 ;
94
- int reduce = 0 ;
95
- int is_ancestor = 0 ;
194
+ int cmdmode = 0 ;
96
195
97
196
struct option options [] = {
98
197
OPT_BOOL ('a' , "all" , & show_all , N_ ("output all common ancestors" )),
99
- OPT_BOOL (0 , "octopus" , & octopus , N_ ("find ancestors for a single n-way merge" )),
100
- OPT_BOOL (0 , "independent" , & reduce , N_ ("list revs not reachable from others" )),
101
- OPT_BOOL (0 , "is-ancestor" , & is_ancestor ,
102
- N_ ("is the first one ancestor of the other?" )),
198
+ OPT_CMDMODE (0 , "octopus" , & cmdmode ,
199
+ N_ ("find ancestors for a single n-way merge" ), 'o' ),
200
+ OPT_CMDMODE (0 , "independent" , & cmdmode ,
201
+ N_ ("list revs not reachable from others" ), 'r' ),
202
+ OPT_CMDMODE (0 , "is-ancestor" , & cmdmode ,
203
+ N_ ("is the first one ancestor of the other?" ), 'a' ),
204
+ OPT_CMDMODE (0 , "fork-point" , & cmdmode ,
205
+ N_ ("find where <commit> forked from reflog of <ref>" ), 'f' ),
103
206
OPT_END ()
104
207
};
105
208
106
209
git_config (git_default_config , NULL );
107
210
argc = parse_options (argc , argv , prefix , options , merge_base_usage , 0 );
108
- if (!octopus && !reduce && argc < 2 )
109
- usage_with_options (merge_base_usage , options );
110
- if (is_ancestor && (show_all || octopus || reduce ))
111
- die ("--is-ancestor cannot be used with other options" );
112
- if (is_ancestor )
211
+
212
+ if (cmdmode == 'a' ) {
213
+ if (argc < 2 )
214
+ usage_with_options (merge_base_usage , options );
215
+ if (show_all )
216
+ die ("--is-ancestor cannot be used with --all" );
113
217
return handle_is_ancestor (argc , argv );
114
- if (reduce && (show_all || octopus ))
115
- die ("--independent cannot be used with other options" );
218
+ }
219
+
220
+ if (cmdmode == 'r' && show_all )
221
+ die ("--independent cannot be used with --all" );
116
222
117
- if (octopus || reduce )
118
- return handle_octopus (argc , argv , reduce , show_all );
223
+ if (cmdmode == 'r' || cmdmode == 'o' )
224
+ return handle_octopus (argc , argv , cmdmode == 'r' , show_all );
225
+
226
+ if (cmdmode == 'f' ) {
227
+ if (argc < 1 || 2 < argc )
228
+ usage_with_options (merge_base_usage , options );
229
+ return handle_fork_point (argc , argv );
230
+ }
231
+
232
+ if (argc < 2 )
233
+ usage_with_options (merge_base_usage , options );
119
234
120
235
rev = xmalloc (argc * sizeof (* rev ));
121
236
while (argc -- > 0 )
0 commit comments