3 #include "parse-options.h"
13 static const char *tag_name;
15 static const char *const pull_request_usage[] = {
16 N_("git request-pull [options] start url [end]"),
20 static int describe_cb(const char *name, const unsigned char *sha1, int flags, void *cb_data)
22 unsigned char peel_sha1[20];
23 if (prefixcmp(name, "refs/tags/"))
25 peel_ref(name, peel_sha1);
26 if (hashcmp(peel_sha1, cb_data))
28 tag_name = skip_prefix(name, "refs/tags/");
32 const char *describe(const char *head)
34 unsigned char sha1[20];
36 for_each_ref(describe_cb, sha1);
40 const char *abbr(const char *name)
43 !prefixcmp(name, "refs/heads/") ? 11 :
44 !prefixcmp(name, "refs/tags/") ? 5 :
48 const char *get_ref(struct transport *transport, const char *head_ref, const unsigned char *head_id)
50 const struct ref *refs, *e;
51 const char *found = NULL;
54 refs = transport_get_remote_refs(transport);
56 for (e = refs; e; e = e->next) {
57 if (hashcmp(e->old_sha1, head_id))
60 deref = !suffixcmp(e->name, "^{}");
61 found = abbr(e->name);
62 if (deref && tag_name && !prefixcmp(e->name + 10, tag_name))
64 if (head_ref && !strcmp(e->name, head_ref))
69 return xstrndup(found, strlen(found) - (deref ? 3 : 0));
72 static const char *show_ident_date(const struct ident_split *ident,
75 unsigned long date = 0;
78 if (ident->date_begin && ident->date_end)
79 date = strtoul(ident->date_begin, NULL, 10);
80 if (ident->tz_begin && ident->tz_end)
81 tz = strtol(ident->tz_begin, NULL, 10);
82 return show_date(date, tz, mode);
85 static void parse_buffer(const char *buffer, const char **summary, const char **date)
87 struct strbuf subject = STRBUF_INIT;
91 c = strstr(buffer, "\ncommitter ") + 11;
93 if (!split_ident_line(&s, c, e - c))
94 *date = show_ident_date(&s, DATE_ISO8601);
96 c = strstr(c, "\n\n") + 2;
97 format_subject(&subject, c, " ");
98 *summary = strbuf_detach(&subject, NULL);
101 static void show_shortlog(const char *base, const char *head)
103 struct commit *commit;
104 struct rev_info revs;
107 struct strbuf tmp = STRBUF_INIT;
109 strbuf_addf(&tmp, "^%s", base);
114 init_revisions(&revs, NULL);
115 setup_revisions(3, args, &revs, NULL);
117 strbuf_release(&tmp);
120 prepare_revision_walk(&revs);
121 while ((commit = get_revision(&revs)))
122 shortlog_add_commit(&log, commit);
123 shortlog_output(&log);
126 static void show_diff(int patch, const unsigned char *base, const unsigned char *head)
128 struct rev_info revs;
130 struct strbuf tmp = STRBUF_INIT;
132 strbuf_addf(&tmp, "^%s", sha1_to_hex(base));
135 args[2] = sha1_to_hex(head);
137 init_revisions(&revs, NULL);
138 setup_revisions(3, args, &revs, NULL);
139 revs.diffopt.stat_width = -1;
140 revs.diffopt.stat_graph_width = -1;
141 revs.diffopt.output_format = patch ? DIFF_FORMAT_PATCH : DIFF_FORMAT_DIFFSTAT;
142 revs.diffopt.output_format |= DIFF_FORMAT_SUMMARY;
143 revs.diffopt.detect_rename = DIFF_DETECT_RENAME;
144 revs.diffopt.flags |= DIFF_OPT_RECURSIVE;
146 strbuf_release(&tmp);
147 diff_tree_sha1(base, head, "", &revs.diffopt);
148 log_tree_diff_flush(&revs);
151 int cmd_request_pull(int argc, const char **argv, const char *prefix)
154 const char *base, *url, *head;
156 char *branch_name = NULL;
157 struct strbuf branch_desc = STRBUF_INIT;
160 unsigned char head_id[20], base_id[20];
161 unsigned char *merge_base_id;
162 struct commit *base_commit, *head_commit, *merge_base_commit;
163 struct commit_list *merge_bases;
164 const char *merge_base_summary, *merge_base_date;
165 const char *head_summary, *head_date;
166 struct remote *remote;
167 struct transport *transport;
169 const struct option options[] = {
170 OPT_BOOL('p', NULL, &patch, N_("show patch text as well")),
174 argc = parse_options(argc, argv, prefix, options, pull_request_usage, 0);
178 head = argv[2] ? argv[2] : "HEAD";
182 usage_with_options(pull_request_usage, options);
184 if (dwim_ref(head, strlen(head), head_id, &head_ref) == 1) {
185 if (!prefixcmp(head_ref, "refs/heads/")) {
186 branch_name = head_ref + 11;
187 if (read_branch_desc(&branch_desc, branch_name) || !branch_desc.len)
194 get_sha1(base, base_id);
195 base_commit = lookup_commit_reference(base_id);
197 die("Not a valid revision: %s", base);
199 head_commit = lookup_commit_reference(head_id);
201 die("Not a valid revision: %s", head);
203 merge_bases = get_merge_bases(base_commit, head_commit, 0);
205 die("No commits in common between %s and %s", base, head);
207 merge_base_commit = merge_bases->item;
208 merge_base_id = merge_base_commit->object.sha1;
210 remote = remote_get(url);
211 url = remote->url[0];
212 transport = transport_get(remote, url);
213 ref = get_ref(transport, strcmp(head_ref, "HEAD") ? head_ref : NULL, head_id);
214 transport_disconnect(transport);
216 parse_buffer(merge_base_commit->buffer, &merge_base_summary, &merge_base_date);
217 parse_buffer(head_commit->buffer, &head_summary, &head_date);
219 printf("The following changes since commit %s:\n"
223 "are available in the git repository at:\n"
225 sha1_to_hex(merge_base_id), merge_base_summary, merge_base_date);
231 "for you to fetch changes up to %s:\n"
235 "----------------------------------------------------------------\n",
236 sha1_to_hex(head_id), head_summary, head_date);
239 printf("(from the branch description for %s local branch)\n\n%s\n", branch_name, branch_desc.buf);
243 enum object_type type;
245 unsigned char sha1[20];
246 const char *begin, *end;
248 if (!ref || prefixcmp(ref, "tags/") || strcmp(ref + 5, tag_name)) {
249 fprintf(stderr, "warn: You locally have %s but it does not (yet)\n", tag_name);
250 fprintf(stderr, "warn: appear to be at %s\n", url);
251 fprintf(stderr, "warn: Do you want to push it there, perhaps?\n");
253 get_sha1(tag_name, sha1);
254 buffer = read_sha1_file(sha1, &type, &size);
255 begin = strstr(buffer, "\n\n") + 2;
256 end = strstr(begin, "-----BEGIN PGP ");
258 end = begin + strlen(begin);
259 printf("%.*s\n", (int)(end - begin), begin);
262 if (branch_name || tag_name)
263 puts("----------------------------------------------------------------");
265 show_shortlog(base, head);
266 show_diff(patch, merge_base_id, head_id);
269 fprintf(stderr, "warn: No branch of %s is at:\n", url);
270 fprintf(stderr, "warn: %s: %s\n", find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary);
271 fprintf(stderr, "warn: Are you sure you pushed '%s' there?\n", head);