]>
Commit | Line | Data |
---|---|---|
fe1a21d5 EN |
1 | /* |
2 | * "git fast-rebase" builtin command | |
3 | * | |
4 | * FAST: Forking Any Subprocesses (is) Taboo | |
5 | * | |
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). | |
11 | */ | |
12 | ||
13 | #define USE_THE_INDEX_COMPATIBILITY_MACROS | |
14 | #include "test-tool.h" | |
15 | ||
16 | #include "cache-tree.h" | |
17 | #include "commit.h" | |
18 | #include "lockfile.h" | |
19 | #include "merge-ort.h" | |
20 | #include "refs.h" | |
21 | #include "revision.h" | |
22 | #include "sequencer.h" | |
23 | #include "strvec.h" | |
24 | #include "tree.h" | |
25 | ||
26 | static const char *short_commit_name(struct commit *commit) | |
27 | { | |
28 | return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV); | |
29 | } | |
30 | ||
31 | static struct commit *peel_committish(const char *name) | |
32 | { | |
33 | struct object *obj; | |
34 | struct object_id oid; | |
35 | ||
36 | if (get_oid(name, &oid)) | |
37 | return NULL; | |
38 | obj = parse_object(the_repository, &oid); | |
39 | return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); | |
40 | } | |
41 | ||
42 | static char *get_author(const char *message) | |
43 | { | |
44 | size_t len; | |
45 | const char *a; | |
46 | ||
47 | a = find_commit_header(message, "author", &len); | |
48 | if (a) | |
49 | return xmemdupz(a, len); | |
50 | ||
51 | return NULL; | |
52 | } | |
53 | ||
54 | static struct commit *create_commit(struct tree *tree, | |
55 | struct commit *based_on, | |
56 | struct commit *parent) | |
57 | { | |
58 | struct object_id ret; | |
59 | struct object *obj; | |
60 | struct commit_list *parents = NULL; | |
61 | char *author; | |
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 }; | |
69 | ||
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); | |
75 | reset_ident_date(); | |
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")); | |
79 | return NULL; | |
80 | } | |
81 | free(author); | |
82 | strbuf_release(&msg); | |
83 | ||
84 | obj = parse_object(the_repository, &ret); | |
85 | return (struct commit *)obj; | |
86 | } | |
87 | ||
88 | int cmd__fast_rebase(int argc, const char **argv) | |
89 | { | |
90 | struct commit *onto; | |
91 | struct commit *last_commit = NULL, *last_picked_commit = NULL; | |
92 | struct object_id head; | |
93 | struct lock_file lock = LOCK_INIT; | |
fe1a21d5 EN |
94 | struct strvec rev_walk_args = STRVEC_INIT; |
95 | struct rev_info revs; | |
96 | struct commit *commit; | |
97 | struct merge_options merge_opt; | |
98 | struct tree *next_tree, *base_tree, *head_tree; | |
99 | struct merge_result result; | |
100 | struct strbuf reflog_msg = STRBUF_INIT; | |
101 | struct strbuf branch_name = STRBUF_INIT; | |
0139c58a | 102 | int ret = 0; |
fe1a21d5 EN |
103 | |
104 | /* | |
105 | * test-tool stuff doesn't set up the git directory by default; need to | |
106 | * do that manually. | |
107 | */ | |
108 | setup_git_directory(); | |
109 | ||
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"); | |
112 | exit(129); | |
113 | } | |
114 | ||
115 | if (argc != 5 || strcmp(argv[1], "--onto")) | |
116 | die("usage: read the code, figure out how to use it, then do so"); | |
117 | ||
118 | onto = peel_committish(argv[2]); | |
119 | strbuf_addf(&branch_name, "refs/heads/%s", argv[4]); | |
120 | ||
121 | /* Sanity check */ | |
122 | if (get_oid("HEAD", &head)) | |
123 | die(_("Cannot read HEAD")); | |
124 | assert(oideq(&onto->object.oid, &head)); | |
125 | ||
126 | hold_locked_index(&lock, LOCK_DIE_ON_ERROR); | |
caba91c3 EN |
127 | if (repo_read_index(the_repository) < 0) |
128 | BUG("Could not read index"); | |
fe1a21d5 EN |
129 | |
130 | repo_init_revisions(the_repository, &revs, NULL); | |
131 | revs.verbose_header = 1; | |
132 | revs.max_parents = 1; | |
133 | revs.cherry_mark = 1; | |
134 | revs.limited = 1; | |
135 | revs.reverse = 1; | |
136 | revs.right_only = 1; | |
137 | revs.sort_order = REV_SORT_IN_GRAPH_ORDER; | |
138 | revs.topo_order = 1; | |
139 | strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL); | |
140 | ||
0139c58a ÆAB |
141 | if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) { |
142 | ret = error(_("unhandled options")); | |
143 | goto cleanup; | |
144 | } | |
fe1a21d5 EN |
145 | |
146 | strvec_clear(&rev_walk_args); | |
147 | ||
0139c58a ÆAB |
148 | if (prepare_revision_walk(&revs) < 0) { |
149 | ret = error(_("error preparing revisions")); | |
150 | goto cleanup; | |
151 | } | |
fe1a21d5 EN |
152 | |
153 | init_merge_options(&merge_opt, the_repository); | |
154 | memset(&result, 0, sizeof(result)); | |
155 | merge_opt.show_rename_progress = 1; | |
156 | merge_opt.branch1 = "HEAD"; | |
157 | head_tree = get_commit_tree(onto); | |
158 | result.tree = head_tree; | |
159 | last_commit = onto; | |
160 | while ((commit = get_revision(&revs))) { | |
161 | struct commit *base; | |
162 | ||
163 | fprintf(stderr, "Rebasing %s...\r", | |
164 | oid_to_hex(&commit->object.oid)); | |
165 | assert(commit->parents && !commit->parents->next); | |
166 | base = commit->parents->item; | |
167 | ||
168 | next_tree = get_commit_tree(commit); | |
169 | base_tree = get_commit_tree(base); | |
170 | ||
171 | merge_opt.branch2 = short_commit_name(commit); | |
172 | merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2); | |
173 | ||
174 | merge_incore_nonrecursive(&merge_opt, | |
175 | base_tree, | |
176 | result.tree, | |
177 | next_tree, | |
178 | &result); | |
179 | ||
180 | free((char*)merge_opt.ancestor); | |
181 | merge_opt.ancestor = NULL; | |
182 | if (!result.clean) | |
f9500261 | 183 | break; |
fe1a21d5 EN |
184 | last_picked_commit = commit; |
185 | last_commit = create_commit(result.tree, commit, last_commit); | |
186 | } | |
fe1a21d5 EN |
187 | |
188 | merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean); | |
189 | ||
190 | if (result.clean < 0) | |
191 | exit(128); | |
192 | ||
f9500261 EN |
193 | if (result.clean) { |
194 | fprintf(stderr, "\nDone.\n"); | |
195 | strbuf_addf(&reflog_msg, "finish rebase %s onto %s", | |
196 | oid_to_hex(&last_picked_commit->object.oid), | |
197 | oid_to_hex(&last_commit->object.oid)); | |
198 | if (update_ref(reflog_msg.buf, branch_name.buf, | |
199 | &last_commit->object.oid, | |
200 | &last_picked_commit->object.oid, | |
201 | REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) { | |
202 | error(_("could not update %s"), argv[4]); | |
203 | die("Failed to update %s", argv[4]); | |
204 | } | |
205 | if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0) | |
206 | die(_("unable to update HEAD")); | |
f9500261 EN |
207 | |
208 | prime_cache_tree(the_repository, the_repository->index, | |
209 | result.tree); | |
210 | } else { | |
211 | fprintf(stderr, "\nAborting: Hit a conflict.\n"); | |
212 | strbuf_addf(&reflog_msg, "rebase progress up to %s", | |
213 | oid_to_hex(&last_picked_commit->object.oid)); | |
214 | if (update_ref(reflog_msg.buf, "HEAD", | |
215 | &last_commit->object.oid, | |
216 | &head, | |
217 | REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) { | |
218 | error(_("could not update %s"), argv[4]); | |
219 | die("Failed to update %s", argv[4]); | |
220 | } | |
fe1a21d5 | 221 | } |
fe1a21d5 EN |
222 | if (write_locked_index(&the_index, &lock, |
223 | COMMIT_LOCK | SKIP_IF_UNCHANGED)) | |
224 | die(_("unable to write %s"), get_index_file()); | |
b925fcf1 | 225 | |
0139c58a ÆAB |
226 | ret = (result.clean == 0); |
227 | cleanup: | |
b925fcf1 ÆAB |
228 | strbuf_release(&reflog_msg); |
229 | strbuf_release(&branch_name); | |
0139c58a ÆAB |
230 | release_revisions(&revs); |
231 | return ret; | |
fe1a21d5 | 232 | } |