2 * "git fast-rebase" builtin command
4 * FAST: Forking Any Subprocesses (is) Taboo
6 * This is meant SOLELY as a demo of what is possible. sequencer.c and
7 * rebase.c should be refactored to use the ideas here, rather than attempting
8 * to extend this file to replace those (unless Phillip or Dscho say that
9 * refactoring is too hard and we need a clean slate, but I'm guessing that
10 * refactoring is the better route).
13 #define USE_THE_INDEX_COMPATIBILITY_MACROS
14 #include "test-tool.h"
16 #include "cache-tree.h"
19 #include "merge-ort.h"
22 #include "sequencer.h"
26 static const char *short_commit_name(struct commit
*commit
)
28 return find_unique_abbrev(&commit
->object
.oid
, DEFAULT_ABBREV
);
31 static struct commit
*peel_committish(const char *name
)
36 if (get_oid(name
, &oid
))
38 obj
= parse_object(the_repository
, &oid
);
39 return (struct commit
*)peel_to_type(name
, 0, obj
, OBJ_COMMIT
);
42 static char *get_author(const char *message
)
47 a
= find_commit_header(message
, "author", &len
);
49 return xmemdupz(a
, len
);
54 static struct commit
*create_commit(struct tree
*tree
,
55 struct commit
*based_on
,
56 struct commit
*parent
)
60 struct commit_list
*parents
= NULL
;
62 char *sign_commit
= NULL
;
63 struct commit_extra_header
*extra
;
64 struct strbuf msg
= STRBUF_INIT
;
65 const char *out_enc
= get_commit_output_encoding();
66 const char *message
= logmsg_reencode(based_on
, NULL
, out_enc
);
67 const char *orig_message
= NULL
;
68 const char *exclude_gpgsig
[] = { "gpgsig", NULL
};
70 commit_list_insert(parent
, &parents
);
71 extra
= read_commit_extra_headers(based_on
, exclude_gpgsig
);
72 find_commit_subject(message
, &orig_message
);
73 strbuf_addstr(&msg
, orig_message
);
74 author
= get_author(message
);
76 if (commit_tree_extended(msg
.buf
, msg
.len
, &tree
->object
.oid
, parents
,
77 &ret
, author
, NULL
, sign_commit
, extra
)) {
78 error(_("failed to write commit object"));
84 obj
= parse_object(the_repository
, &ret
);
85 return (struct commit
*)obj
;
88 int cmd__fast_rebase(int argc
, const char **argv
)
91 struct commit
*last_commit
= NULL
, *last_picked_commit
= NULL
;
92 struct object_id head
;
93 struct lock_file lock
= LOCK_INIT
;
95 struct strvec rev_walk_args
= STRVEC_INIT
;
97 struct commit
*commit
;
98 struct merge_options merge_opt
;
99 struct tree
*next_tree
, *base_tree
, *head_tree
;
100 struct merge_result result
;
101 struct strbuf reflog_msg
= STRBUF_INIT
;
102 struct strbuf branch_name
= STRBUF_INIT
;
105 * test-tool stuff doesn't set up the git directory by default; need to
108 setup_git_directory();
110 if (argc
== 2 && !strcmp(argv
[1], "-h")) {
111 printf("Sorry, I am not a psychiatrist; I can not give you the help you need. Oh, you meant usage...\n");
115 if (argc
!= 5 || strcmp(argv
[1], "--onto"))
116 die("usage: read the code, figure out how to use it, then do so");
118 onto
= peel_committish(argv
[2]);
119 strbuf_addf(&branch_name
, "refs/heads/%s", argv
[4]);
122 if (get_oid("HEAD", &head
))
123 die(_("Cannot read HEAD"));
124 assert(oideq(&onto
->object
.oid
, &head
));
126 hold_locked_index(&lock
, LOCK_DIE_ON_ERROR
);
127 assert(repo_read_index(the_repository
) >= 0);
129 repo_init_revisions(the_repository
, &revs
, NULL
);
130 revs
.verbose_header
= 1;
131 revs
.max_parents
= 1;
132 revs
.cherry_mark
= 1;
136 revs
.sort_order
= REV_SORT_IN_GRAPH_ORDER
;
138 strvec_pushl(&rev_walk_args
, "", argv
[4], "--not", argv
[3], NULL
);
140 if (setup_revisions(rev_walk_args
.nr
, rev_walk_args
.v
, &revs
, NULL
) > 1)
141 return error(_("unhandled options"));
143 strvec_clear(&rev_walk_args
);
145 if (prepare_revision_walk(&revs
) < 0)
146 return error(_("error preparing revisions"));
148 init_merge_options(&merge_opt
, the_repository
);
149 memset(&result
, 0, sizeof(result
));
150 merge_opt
.show_rename_progress
= 1;
151 merge_opt
.branch1
= "HEAD";
152 head_tree
= get_commit_tree(onto
);
153 result
.tree
= head_tree
;
155 while ((commit
= get_revision(&revs
))) {
158 fprintf(stderr
, "Rebasing %s...\r",
159 oid_to_hex(&commit
->object
.oid
));
160 assert(commit
->parents
&& !commit
->parents
->next
);
161 base
= commit
->parents
->item
;
163 next_tree
= get_commit_tree(commit
);
164 base_tree
= get_commit_tree(base
);
166 merge_opt
.branch2
= short_commit_name(commit
);
167 merge_opt
.ancestor
= xstrfmt("parent of %s", merge_opt
.branch2
);
169 merge_incore_nonrecursive(&merge_opt
,
175 free((char*)merge_opt
.ancestor
);
176 merge_opt
.ancestor
= NULL
;
178 die("Aborting: Hit a conflict and restarting is not implemented.");
179 last_picked_commit
= commit
;
180 last_commit
= create_commit(result
.tree
, commit
, last_commit
);
182 fprintf(stderr
, "\nDone.\n");
183 /* TODO: There should be some kind of rev_info_free(&revs) call... */
184 memset(&revs
, 0, sizeof(revs
));
186 merge_switch_to_result(&merge_opt
, head_tree
, &result
, 1, !result
.clean
);
188 if (result
.clean
< 0)
191 strbuf_addf(&reflog_msg
, "finish rebase %s onto %s",
192 oid_to_hex(&last_picked_commit
->object
.oid
),
193 oid_to_hex(&last_commit
->object
.oid
));
194 if (update_ref(reflog_msg
.buf
, branch_name
.buf
,
195 &last_commit
->object
.oid
,
196 &last_picked_commit
->object
.oid
,
197 REF_NO_DEREF
, UPDATE_REFS_MSG_ON_ERR
)) {
198 error(_("could not update %s"), argv
[4]);
199 die("Failed to update %s", argv
[4]);
201 if (create_symref("HEAD", branch_name
.buf
, reflog_msg
.buf
) < 0)
202 die(_("unable to update HEAD"));
203 strbuf_release(&reflog_msg
);
204 strbuf_release(&branch_name
);
206 prime_cache_tree(the_repository
, the_repository
->index
, result
.tree
);
207 if (write_locked_index(&the_index
, &lock
,
208 COMMIT_LOCK
| SKIP_IF_UNCHANGED
))
209 die(_("unable to write %s"), get_index_file());