9
9
#include "exec-cmd.h"
10
10
#include "argv-array.h"
11
11
#include "dir.h"
12
+ #include "packfile.h"
13
+ #include "checkout.h"
14
+ #include "refs.h"
15
+ #include "quote.h"
16
+
17
+ static GIT_PATH_FUNC (apply_dir , "rebase-apply" );
18
+ static GIT_PATH_FUNC (merge_dir , "rebase-merge" );
19
+
20
+ enum rebase_type {
21
+ REBASE_AM ,
22
+ REBASE_MERGE ,
23
+ REBASE_INTERACTIVE ,
24
+ REBASE_PRESERVE_MERGES
25
+ };
12
26
13
27
static int use_builtin_rebase (void )
14
28
{
@@ -28,8 +42,136 @@ static int use_builtin_rebase(void)
28
42
return ret ;
29
43
}
30
44
45
+ static int apply_autostash (void )
46
+ {
47
+ warning ("TODO" );
48
+ return 0 ;
49
+ }
50
+
51
+ struct rebase_options {
52
+ enum rebase_type type ;
53
+ const char * state_dir ;
54
+ struct commit * upstream ;
55
+ const char * upstream_name ;
56
+ char * head_name ;
57
+ struct object_id orig_head ;
58
+ struct commit * onto ;
59
+ const char * onto_name ;
60
+ const char * revisions ;
61
+ const char * root ;
62
+ };
63
+
64
+ static int finish_rebase (struct rebase_options * opts )
65
+ {
66
+ struct strbuf dir = STRBUF_INIT ;
67
+ const char * argv_gc_auto [] = { "gc" , "--auto" , NULL };
68
+
69
+ delete_ref (NULL , "REBASE_HEAD" , NULL , REF_NO_DEREF );
70
+ apply_autostash ();
71
+ close_all_packs (the_repository -> objects );
72
+ /*
73
+ * We ignore errors in 'gc --auto', since the
74
+ * user should see them.
75
+ */
76
+ run_command_v_opt (argv_gc_auto , RUN_GIT_CMD );
77
+ strbuf_addstr (& dir , opts -> state_dir );
78
+ remove_dir_recursively (& dir , 0 );
79
+ strbuf_release (& dir );
80
+
81
+ return 0 ;
82
+ }
83
+
84
+ static struct commit * peel_committish (const char * name )
85
+ {
86
+ struct object * obj ;
87
+ struct object_id oid ;
88
+
89
+ if (get_oid (name , & oid ))
90
+ return NULL ;
91
+ obj = parse_object (& oid );
92
+ return (struct commit * )peel_to_type (name , 0 , obj , OBJ_COMMIT );
93
+ }
94
+
95
+ static void add_var (struct strbuf * buf , const char * name , const char * value )
96
+ {
97
+ strbuf_addstr (buf , name );
98
+ strbuf_addstr (buf , "=" );
99
+ sq_quote_buf (buf , value );
100
+ strbuf_addstr (buf , "; " );
101
+ }
102
+
103
+ static int run_specific_rebase (struct rebase_options * opts )
104
+ {
105
+ const char * argv [] = { NULL , NULL };
106
+ struct strbuf script_snippet = STRBUF_INIT ;
107
+ int status ;
108
+ const char * backend , * backend_func ;
109
+
110
+ add_var (& script_snippet , "GIT_DIR" , absolute_path (get_git_dir ()));
111
+
112
+ add_var (& script_snippet , "upstream_name" , opts -> upstream_name );
113
+ add_var (& script_snippet , "upstream" ,
114
+ oid_to_hex (& opts -> upstream -> object .oid ));
115
+ add_var (& script_snippet , "head_name" , opts -> head_name );
116
+ add_var (& script_snippet , "orig_head" , oid_to_hex (& opts -> orig_head ));
117
+ add_var (& script_snippet , "onto" , oid_to_hex (& opts -> onto -> object .oid ));
118
+ add_var (& script_snippet , "onto_name" , opts -> onto_name );
119
+ add_var (& script_snippet , "revisions" , opts -> revisions );
120
+
121
+ switch (opts -> type ) {
122
+ case REBASE_AM :
123
+ backend = "git-rebase--am" ;
124
+ backend_func = "git_rebase__am" ;
125
+ break ;
126
+ case REBASE_INTERACTIVE :
127
+ backend = "git-rebase--interactive" ;
128
+ backend_func = "git_rebase__interactive" ;
129
+ break ;
130
+ case REBASE_MERGE :
131
+ backend = "git-rebase--merge" ;
132
+ backend_func = "git_rebase__merge" ;
133
+ break ;
134
+ case REBASE_PRESERVE_MERGES :
135
+ backend = "git-rebase--preserve-merges" ;
136
+ backend_func = "git_rebase__preserve_merges" ;
137
+ break ;
138
+ default :
139
+ BUG ("Unhandled rebase type %d" , opts -> type );
140
+ break ;
141
+ }
142
+
143
+ strbuf_addf (& script_snippet ,
144
+ ". git-rebase--common && . %s && %s" ,
145
+ backend , backend_func );
146
+ argv [0 ] = script_snippet .buf ;
147
+
148
+ status = run_command_v_opt (argv , RUN_USING_SHELL );
149
+ if (status == 0 )
150
+ finish_rebase (opts );
151
+ else if (status == 2 ) {
152
+ struct strbuf dir = STRBUF_INIT ;
153
+
154
+ apply_autostash ();
155
+ strbuf_addstr (& dir , opts -> state_dir );
156
+ remove_dir_recursively (& dir , 0 );
157
+ strbuf_release (& dir );
158
+ die ("Nothing to do" );
159
+ }
160
+
161
+ strbuf_release (& script_snippet );
162
+
163
+ return status ? -1 : 0 ;
164
+ }
165
+
31
166
int cmd_rebase (int argc , const char * * argv , const char * prefix )
32
167
{
168
+ struct rebase_options options = { -1 };
169
+ const char * branch_name ;
170
+ int ret , flags , quiet = 0 ;
171
+ struct strbuf msg = STRBUF_INIT ;
172
+ struct strbuf revisions = STRBUF_INIT ;
173
+ const char * restrict_revision = NULL ;
174
+
33
175
/*
34
176
* NEEDSWORK: Once the builtin rebase has been tested enough
35
177
* and git-legacy-rebase.sh is retired to contrib/, this preamble
@@ -52,5 +194,99 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
52
194
trace_repo_setup (prefix );
53
195
setup_work_tree ();
54
196
55
- die ("TODO" );
197
+ options .type = REBASE_AM ;
198
+
199
+ switch (options .type ) {
200
+ case REBASE_AM :
201
+ options .state_dir = apply_dir ();
202
+ break ;
203
+ case REBASE_MERGE :
204
+ case REBASE_INTERACTIVE :
205
+ case REBASE_PRESERVE_MERGES :
206
+ options .state_dir = merge_dir ();
207
+ break ;
208
+ }
209
+ if (!options .root ) {
210
+ if (argc != 2 )
211
+ die ("TODO: handle @{upstream}" );
212
+ else {
213
+ options .upstream_name = argv [1 ];
214
+ argc -- ;
215
+ argv ++ ;
216
+ if (!strcmp (options .upstream_name , "-" ))
217
+ options .upstream_name = "@{-1}" ;
218
+ }
219
+ options .upstream = peel_committish (options .upstream_name );
220
+ if (!options .upstream )
221
+ die (_ ("invalid upstream '%s'" ), options .upstream_name );
222
+ } else
223
+ die ("TODO: upstream for --root" );
224
+
225
+ /* Make sure the branch to rebase onto is valid. */
226
+ if (!options .onto_name )
227
+ options .onto_name = options .upstream_name ;
228
+ if (strstr (options .onto_name , "..." )) {
229
+ die ("TODO" );
230
+ } else {
231
+ options .onto = peel_committish (options .onto_name );
232
+ if (!options .onto )
233
+ die (_ ("Does not point to a valid commit '%s'" ),
234
+ options .onto_name );
235
+ }
236
+
237
+ /*
238
+ * If the branch to rebase is given, that is the branch we will rebase
239
+ * branch_name -- branch/commit being rebased, or
240
+ * HEAD (already detached)
241
+ * orig_head -- commit object name of tip of the branch before rebasing
242
+ * head_name -- refs/heads/<that-branch> or "detached HEAD"
243
+ */
244
+ if (argc > 1 )
245
+ die ("TODO: handle switch_to" );
246
+ else {
247
+ /* Do not need to switch branches, we are already on it. */
248
+ options .head_name =
249
+ xstrdup_or_null (resolve_ref_unsafe ("HEAD" , 0 , NULL ,
250
+ & flags ));
251
+ if (!options .head_name )
252
+ die (_ ("No such ref: %s" ), "HEAD" );
253
+ if (flags & REF_ISSYMREF ) {
254
+ if (!skip_prefix (options .head_name ,
255
+ "refs/heads/" , & branch_name ))
256
+ branch_name = options .head_name ;
257
+
258
+ } else {
259
+ options .head_name = xstrdup ("detached HEAD" );
260
+ branch_name = "HEAD" ;
261
+ }
262
+ if (get_oid ("HEAD" , & options .orig_head ))
263
+ die (_ ("Could not resolve HEAD to a revision" ));
264
+ }
265
+
266
+ /* Detach HEAD and reset the tree */
267
+ if (!quiet )
268
+ printf ("First, rewinding head to replay your work on top of it..." );
269
+
270
+ strbuf_addf (& msg , "rebase: checkout %s" , options .onto_name );
271
+ if (detach_head_to (& options .onto -> object .oid , "checkout" , msg .buf ))
272
+ die (_ ("Could not detach HEAD" ));
273
+ strbuf_release (& msg );
274
+ if (update_ref ("rebase" , "ORIG_HEAD" , & options .orig_head , NULL , 0 ,
275
+ UPDATE_REFS_MSG_ON_ERR ) < 0 )
276
+ die (_ ("Could not update ORIG_HEAD to '%s'" ),
277
+ oid_to_hex (& options .orig_head ));
278
+
279
+ strbuf_addf (& revisions , "%s..%s" ,
280
+ !options .root ? oid_to_hex (& options .onto -> object .oid ) :
281
+ (restrict_revision ? restrict_revision :
282
+ oid_to_hex (& options .upstream -> object .oid )),
283
+ oid_to_hex (& options .orig_head ));
284
+
285
+ options .revisions = revisions .buf ;
286
+
287
+ ret = !!run_specific_rebase (& options );
288
+
289
+ strbuf_release (& revisions );
290
+ free (options .head_name );
291
+ return ret ;
56
292
}
0 commit comments