99#include "exec-cmd.h"
1010#include "argv-array.h"
1111#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+ };
1230
1331static int use_builtin_rebase (void )
1432{
@@ -30,8 +48,260 @@ static int use_builtin_rebase(void)
3048 return ret ;
3149}
3250
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+
33295int cmd_rebase (int argc , const char * * argv , const char * prefix )
34296{
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+
35305 /*
36306 * NEEDSWORK: Once the builtin rebase has been tested enough
37307 * 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)
54324 trace_repo_setup (prefix );
55325 setup_work_tree ();
56326
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 ;
58421}
0 commit comments