9
9
#include "exec-cmd.h"
10
10
#include "argv-array.h"
11
11
#include "dir.h"
12
+ #include "packfile.h"
13
+ #include "refs.h"
14
+ #include "quote.h"
15
+ #include "config.h"
16
+ #include "cache-tree.h"
17
+ #include "unpack-trees.h"
18
+ #include "lockfile.h"
19
+
20
+ static GIT_PATH_FUNC (apply_dir , "rebase-apply" )
21
+ static GIT_PATH_FUNC (merge_dir , "rebase-merge" )
22
+
23
+ enum rebase_type {
24
+ REBASE_UNSPECIFIED = -1 ,
25
+ REBASE_AM ,
26
+ REBASE_MERGE ,
27
+ REBASE_INTERACTIVE ,
28
+ REBASE_PRESERVE_MERGES
29
+ };
12
30
13
31
static int use_builtin_rebase (void )
14
32
{
@@ -30,8 +48,260 @@ static int use_builtin_rebase(void)
30
48
return ret ;
31
49
}
32
50
51
+ static int apply_autostash (void )
52
+ {
53
+ warning ("TODO" );
54
+ return 0 ;
55
+ }
56
+
57
+ struct rebase_options {
58
+ enum rebase_type type ;
59
+ const char * state_dir ;
60
+ struct commit * upstream ;
61
+ const char * upstream_name ;
62
+ char * head_name ;
63
+ struct object_id orig_head ;
64
+ struct commit * onto ;
65
+ const char * onto_name ;
66
+ const char * revisions ;
67
+ int root ;
68
+ struct commit * restrict_revision ;
69
+ int dont_finish_rebase ;
70
+ };
71
+
72
+ /* Returns the filename prefixed by the state_dir */
73
+ static const char * state_dir_path (const char * filename , struct rebase_options * opts )
74
+ {
75
+ static struct strbuf path = STRBUF_INIT ;
76
+ static size_t prefix_len ;
77
+
78
+ if (!prefix_len ) {
79
+ strbuf_addf (& path , "%s/" , opts -> state_dir );
80
+ prefix_len = path .len ;
81
+ }
82
+
83
+ strbuf_setlen (& path , prefix_len );
84
+ strbuf_addstr (& path , filename );
85
+ return path .buf ;
86
+ }
87
+
88
+ static int finish_rebase (struct rebase_options * opts )
89
+ {
90
+ struct strbuf dir = STRBUF_INIT ;
91
+ const char * argv_gc_auto [] = { "gc" , "--auto" , NULL };
92
+
93
+ delete_ref (NULL , "REBASE_HEAD" , NULL , REF_NO_DEREF );
94
+ apply_autostash ();
95
+ close_all_packs (the_repository -> objects );
96
+ /*
97
+ * We ignore errors in 'gc --auto', since the
98
+ * user should see them.
99
+ */
100
+ run_command_v_opt (argv_gc_auto , RUN_GIT_CMD );
101
+ strbuf_addstr (& dir , opts -> state_dir );
102
+ remove_dir_recursively (& dir , 0 );
103
+ strbuf_release (& dir );
104
+
105
+ return 0 ;
106
+ }
107
+
108
+ static struct commit * peel_committish (const char * name )
109
+ {
110
+ struct object * obj ;
111
+ struct object_id oid ;
112
+
113
+ if (get_oid (name , & oid ))
114
+ return NULL ;
115
+ obj = parse_object (the_repository , & oid );
116
+ return (struct commit * )peel_to_type (name , 0 , obj , OBJ_COMMIT );
117
+ }
118
+
119
+ static void add_var (struct strbuf * buf , const char * name , const char * value )
120
+ {
121
+ if (!value )
122
+ strbuf_addf (buf , "unset %s; " , name );
123
+ else {
124
+ strbuf_addf (buf , "%s=" , name );
125
+ sq_quote_buf (buf , value );
126
+ strbuf_addstr (buf , "; " );
127
+ }
128
+ }
129
+
130
+ static int run_specific_rebase (struct rebase_options * opts )
131
+ {
132
+ const char * argv [] = { NULL , NULL };
133
+ struct strbuf script_snippet = STRBUF_INIT ;
134
+ int status ;
135
+ const char * backend , * backend_func ;
136
+
137
+ add_var (& script_snippet , "GIT_DIR" , absolute_path (get_git_dir ()));
138
+ add_var (& script_snippet , "state_dir" , opts -> state_dir );
139
+
140
+ add_var (& script_snippet , "upstream_name" , opts -> upstream_name );
141
+ add_var (& script_snippet , "upstream" ,
142
+ oid_to_hex (& opts -> upstream -> object .oid ));
143
+ add_var (& script_snippet , "head_name" , opts -> head_name );
144
+ add_var (& script_snippet , "orig_head" , oid_to_hex (& opts -> orig_head ));
145
+ add_var (& script_snippet , "onto" , oid_to_hex (& opts -> onto -> object .oid ));
146
+ add_var (& script_snippet , "onto_name" , opts -> onto_name );
147
+ add_var (& script_snippet , "revisions" , opts -> revisions );
148
+ add_var (& script_snippet , "restrict_revision" , opts -> restrict_revision ?
149
+ oid_to_hex (& opts -> restrict_revision -> object .oid ) : NULL );
150
+
151
+ switch (opts -> type ) {
152
+ case REBASE_AM :
153
+ backend = "git-rebase--am" ;
154
+ backend_func = "git_rebase__am" ;
155
+ break ;
156
+ case REBASE_INTERACTIVE :
157
+ backend = "git-rebase--interactive" ;
158
+ backend_func = "git_rebase__interactive" ;
159
+ break ;
160
+ case REBASE_MERGE :
161
+ backend = "git-rebase--merge" ;
162
+ backend_func = "git_rebase__merge" ;
163
+ break ;
164
+ case REBASE_PRESERVE_MERGES :
165
+ backend = "git-rebase--preserve-merges" ;
166
+ backend_func = "git_rebase__preserve_merges" ;
167
+ break ;
168
+ default :
169
+ BUG ("Unhandled rebase type %d" , opts -> type );
170
+ break ;
171
+ }
172
+
173
+ strbuf_addf (& script_snippet ,
174
+ ". git-sh-setup && . git-rebase--common &&"
175
+ " . %s && %s" , backend , backend_func );
176
+ argv [0 ] = script_snippet .buf ;
177
+
178
+ status = run_command_v_opt (argv , RUN_USING_SHELL );
179
+ if (opts -> dont_finish_rebase )
180
+ ; /* do nothing */
181
+ else if (status == 0 ) {
182
+ if (!file_exists (state_dir_path ("stopped-sha" , opts )))
183
+ finish_rebase (opts );
184
+ } else if (status == 2 ) {
185
+ struct strbuf dir = STRBUF_INIT ;
186
+
187
+ apply_autostash ();
188
+ strbuf_addstr (& dir , opts -> state_dir );
189
+ remove_dir_recursively (& dir , 0 );
190
+ strbuf_release (& dir );
191
+ die ("Nothing to do" );
192
+ }
193
+
194
+ strbuf_release (& script_snippet );
195
+
196
+ return status ? -1 : 0 ;
197
+ }
198
+
199
+ #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
200
+
201
+ static int reset_head (struct object_id * oid , const char * action ,
202
+ const char * switch_to_branch , int detach_head )
203
+ {
204
+ struct object_id head_oid ;
205
+ struct tree_desc desc ;
206
+ struct lock_file lock = LOCK_INIT ;
207
+ struct unpack_trees_options unpack_tree_opts ;
208
+ struct tree * tree ;
209
+ const char * reflog_action ;
210
+ struct strbuf msg = STRBUF_INIT ;
211
+ size_t prefix_len ;
212
+ struct object_id * orig = NULL , oid_orig ,
213
+ * old_orig = NULL , oid_old_orig ;
214
+ int ret = 0 ;
215
+
216
+ if (hold_locked_index (& lock , LOCK_REPORT_ON_ERROR ) < 0 )
217
+ return -1 ;
218
+
219
+ if (!oid ) {
220
+ if (get_oid ("HEAD" , & head_oid )) {
221
+ rollback_lock_file (& lock );
222
+ return error (_ ("could not determine HEAD revision" ));
223
+ }
224
+ oid = & head_oid ;
225
+ }
226
+
227
+ memset (& unpack_tree_opts , 0 , sizeof (unpack_tree_opts ));
228
+ setup_unpack_trees_porcelain (& unpack_tree_opts , action );
229
+ unpack_tree_opts .head_idx = 1 ;
230
+ unpack_tree_opts .src_index = the_repository -> index ;
231
+ unpack_tree_opts .dst_index = the_repository -> index ;
232
+ unpack_tree_opts .fn = oneway_merge ;
233
+ unpack_tree_opts .update = 1 ;
234
+ unpack_tree_opts .merge = 1 ;
235
+ if (!detach_head )
236
+ unpack_tree_opts .reset = 1 ;
237
+
238
+ if (read_index_unmerged (the_repository -> index ) < 0 ) {
239
+ rollback_lock_file (& lock );
240
+ return error (_ ("could not read index" ));
241
+ }
242
+
243
+ if (!fill_tree_descriptor (& desc , oid )) {
244
+ error (_ ("failed to find tree of %s" ), oid_to_hex (oid ));
245
+ rollback_lock_file (& lock );
246
+ free ((void * )desc .buffer );
247
+ return -1 ;
248
+ }
249
+
250
+ if (unpack_trees (1 , & desc , & unpack_tree_opts )) {
251
+ rollback_lock_file (& lock );
252
+ free ((void * )desc .buffer );
253
+ return -1 ;
254
+ }
255
+
256
+ tree = parse_tree_indirect (oid );
257
+ prime_cache_tree (the_repository -> index , tree );
258
+
259
+ if (write_locked_index (the_repository -> index , & lock , COMMIT_LOCK ) < 0 )
260
+ ret = error (_ ("could not write index" ));
261
+ free ((void * )desc .buffer );
262
+
263
+ if (ret )
264
+ return ret ;
265
+
266
+ reflog_action = getenv (GIT_REFLOG_ACTION_ENVIRONMENT );
267
+ strbuf_addf (& msg , "%s: " , reflog_action ? reflog_action : "rebase" );
268
+ prefix_len = msg .len ;
269
+
270
+ if (!get_oid ("ORIG_HEAD" , & oid_old_orig ))
271
+ old_orig = & oid_old_orig ;
272
+ if (!get_oid ("HEAD" , & oid_orig )) {
273
+ orig = & oid_orig ;
274
+ strbuf_addstr (& msg , "updating ORIG_HEAD" );
275
+ update_ref (msg .buf , "ORIG_HEAD" , orig , old_orig , 0 ,
276
+ UPDATE_REFS_MSG_ON_ERR );
277
+ } else if (old_orig )
278
+ delete_ref (NULL , "ORIG_HEAD" , old_orig , 0 );
279
+ strbuf_setlen (& msg , prefix_len );
280
+ strbuf_addstr (& msg , "updating HEAD" );
281
+ if (!switch_to_branch )
282
+ ret = update_ref (msg .buf , "HEAD" , oid , orig , REF_NO_DEREF ,
283
+ UPDATE_REFS_MSG_ON_ERR );
284
+ else {
285
+ ret = create_symref ("HEAD" , switch_to_branch , msg .buf );
286
+ if (!ret )
287
+ ret = update_ref (msg .buf , "HEAD" , oid , NULL , 0 ,
288
+ UPDATE_REFS_MSG_ON_ERR );
289
+ }
290
+
291
+ strbuf_release (& msg );
292
+ return ret ;
293
+ }
294
+
33
295
int cmd_rebase (int argc , const char * * argv , const char * prefix )
34
296
{
297
+ struct rebase_options options = {
298
+ .type = REBASE_UNSPECIFIED ,
299
+ };
300
+ const char * branch_name ;
301
+ int ret , flags ;
302
+ struct strbuf msg = STRBUF_INIT ;
303
+ struct strbuf revisions = STRBUF_INIT ;
304
+
35
305
/*
36
306
* NEEDSWORK: Once the builtin rebase has been tested enough
37
307
* and git-legacy-rebase.sh is retired to contrib/, this preamble
@@ -54,5 +324,98 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
54
324
trace_repo_setup (prefix );
55
325
setup_work_tree ();
56
326
57
- die ("TODO" );
327
+ git_config (git_default_config , NULL );
328
+
329
+ switch (options .type ) {
330
+ case REBASE_MERGE :
331
+ case REBASE_INTERACTIVE :
332
+ case REBASE_PRESERVE_MERGES :
333
+ options .state_dir = merge_dir ();
334
+ break ;
335
+ case REBASE_AM :
336
+ options .state_dir = apply_dir ();
337
+ break ;
338
+ default :
339
+ /* the default rebase backend is `--am` */
340
+ options .type = REBASE_AM ;
341
+ options .state_dir = apply_dir ();
342
+ break ;
343
+ }
344
+
345
+ if (!options .root ) {
346
+ if (argc < 2 )
347
+ die ("TODO: handle @{upstream}" );
348
+ else {
349
+ options .upstream_name = argv [1 ];
350
+ argc -- ;
351
+ argv ++ ;
352
+ if (!strcmp (options .upstream_name , "-" ))
353
+ options .upstream_name = "@{-1}" ;
354
+ }
355
+ options .upstream = peel_committish (options .upstream_name );
356
+ if (!options .upstream )
357
+ die (_ ("invalid upstream '%s'" ), options .upstream_name );
358
+ } else
359
+ die ("TODO: upstream for --root" );
360
+
361
+ /* Make sure the branch to rebase onto is valid. */
362
+ if (!options .onto_name )
363
+ options .onto_name = options .upstream_name ;
364
+ if (strstr (options .onto_name , "..." )) {
365
+ die ("TODO" );
366
+ } else {
367
+ options .onto = peel_committish (options .onto_name );
368
+ if (!options .onto )
369
+ die (_ ("Does not point to a valid commit '%s'" ),
370
+ options .onto_name );
371
+ }
372
+
373
+ /*
374
+ * If the branch to rebase is given, that is the branch we will rebase
375
+ * branch_name -- branch/commit being rebased, or
376
+ * HEAD (already detached)
377
+ * orig_head -- commit object name of tip of the branch before rebasing
378
+ * head_name -- refs/heads/<that-branch> or "detached HEAD"
379
+ */
380
+ if (argc > 1 )
381
+ die ("TODO: handle switch_to" );
382
+ else {
383
+ /* Do not need to switch branches, we are already on it. */
384
+ options .head_name =
385
+ xstrdup_or_null (resolve_ref_unsafe ("HEAD" , 0 , NULL ,
386
+ & flags ));
387
+ if (!options .head_name )
388
+ die (_ ("No such ref: %s" ), "HEAD" );
389
+ if (flags & REF_ISSYMREF ) {
390
+ if (!skip_prefix (options .head_name ,
391
+ "refs/heads/" , & branch_name ))
392
+ branch_name = options .head_name ;
393
+
394
+ } else {
395
+ options .head_name = xstrdup ("detached HEAD" );
396
+ branch_name = "HEAD" ;
397
+ }
398
+ if (get_oid ("HEAD" , & options .orig_head ))
399
+ die (_ ("Could not resolve HEAD to a revision" ));
400
+ }
401
+
402
+ strbuf_addf (& msg , "rebase: checkout %s" , options .onto_name );
403
+ if (reset_head (& options .onto -> object .oid , "checkout" , NULL , 1 ))
404
+ die (_ ("Could not detach HEAD" ));
405
+ strbuf_release (& msg );
406
+
407
+ strbuf_addf (& revisions , "%s..%s" ,
408
+ options .root ? oid_to_hex (& options .onto -> object .oid ) :
409
+ (options .restrict_revision ?
410
+ oid_to_hex (& options .restrict_revision -> object .oid ) :
411
+ oid_to_hex (& options .upstream -> object .oid )),
412
+ oid_to_hex (& options .orig_head ));
413
+
414
+ options .revisions = revisions .buf ;
415
+
416
+ ret = !!run_specific_rebase (& options );
417
+
418
+ strbuf_release (& revisions );
419
+ free (options .head_name );
420
+ return ret ;
58
421
}
0 commit comments