[PATCH v2 44/44] request-pull: rewrite to C

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Felipe Contreras <felipe.contreras@xxxxxxxxx>
---
 Makefile               |   3 +-
 builtin.h              |   1 +
 builtin/request-pull.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++
 git-request-pull.rb    | 199 -----------------------------------
 git.c                  |   1 +
 5 files changed, 278 insertions(+), 201 deletions(-)
 create mode 100644 builtin/request-pull.c
 delete mode 100755 git-request-pull.rb

diff --git a/Makefile b/Makefile
index 709c087..edbceea 100644
--- a/Makefile
+++ b/Makefile
@@ -492,9 +492,7 @@ SCRIPT_PYTHON += git-remote-testpy.py
 SCRIPT_PYTHON += git-p4.py
 
 SCRIPT_RUBY += git-rb-setup.rb
-SCRIPT_RUBY += git-request-pull.rb
 
-RUBY_PROGRAMS += git-request-pull$X
 PROGRAMS += $(RUBY_PROGRAMS)
 
 NO_INSTALL += git-remote-testgit
@@ -983,6 +981,7 @@ BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/remote-ext.o
 BUILTIN_OBJS += builtin/remote-fd.o
 BUILTIN_OBJS += builtin/replace.o
+BUILTIN_OBJS += builtin/request-pull.o
 BUILTIN_OBJS += builtin/rerere.o
 BUILTIN_OBJS += builtin/reset.o
 BUILTIN_OBJS += builtin/rev-list.o
diff --git a/builtin.h b/builtin.h
index 8afa2de..7db356e 100644
--- a/builtin.h
+++ b/builtin.h
@@ -103,6 +103,7 @@ extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
+extern int cmd_request_pull(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
diff --git a/builtin/request-pull.c b/builtin/request-pull.c
new file mode 100644
index 0000000..a8f7f04
--- /dev/null
+++ b/builtin/request-pull.c
@@ -0,0 +1,275 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "revision.h"
+#include "refs.h"
+#include "remote.h"
+#include "transport.h"
+#include "branch.h"
+#include "shortlog.h"
+#include "diff.h"
+#include "log-tree.h"
+
+static const char *tag_name;
+
+static const char *const pull_request_usage[] = {
+	N_("git request-pull [options] start url [end]"),
+	NULL
+};
+
+static int describe_cb(const char *name, const unsigned char *sha1, int flags, void *cb_data)
+{
+	unsigned char peel_sha1[20];
+	if (prefixcmp(name, "refs/tags/"))
+		return 0;
+	peel_ref(name, peel_sha1);
+	if (hashcmp(peel_sha1, cb_data))
+		return 0;
+	tag_name = skip_prefix(name, "refs/tags/");
+	return 1;
+}
+
+const char *describe(const char *head)
+{
+	unsigned char sha1[20];
+	get_sha1(head, sha1);
+	for_each_ref(describe_cb, sha1);
+	return tag_name;
+}
+
+const char *abbr(const char *name)
+{
+	return name + (
+			!prefixcmp(name, "refs/heads/") ? 11 :
+			!prefixcmp(name, "refs/tags/") ? 5 :
+			0);
+}
+
+const char *get_ref(struct transport *transport, const char *head_ref, const unsigned char *head_id)
+{
+	const struct ref *refs, *e;
+	const char *found = NULL;
+	int deref;
+
+	refs = transport_get_remote_refs(transport);
+
+	for (e = refs; e; e = e->next) {
+		if (hashcmp(e->old_sha1, head_id))
+			continue;
+
+		deref = !suffixcmp(e->name, "^{}");
+		found = abbr(e->name);
+		if (deref && tag_name && !prefixcmp(e->name + 10, tag_name))
+			break;
+		if (head_ref && !strcmp(e->name, head_ref))
+			break;
+	}
+	if (!found)
+		return NULL;
+	return xstrndup(found, strlen(found) - (deref ? 3 : 0));
+}
+
+static const char *show_ident_date(const struct ident_split *ident,
+				   enum date_mode mode)
+{
+	unsigned long date = 0;
+	int tz = 0;
+
+	if (ident->date_begin && ident->date_end)
+		date = strtoul(ident->date_begin, NULL, 10);
+	if (ident->tz_begin && ident->tz_end)
+		tz = strtol(ident->tz_begin, NULL, 10);
+	return show_date(date, tz, mode);
+}
+
+static void parse_buffer(const char *buffer, const char **summary, const char **date)
+{
+	struct strbuf subject = STRBUF_INIT;
+	struct ident_split s;
+	const char *c, *e;
+
+	c = strstr(buffer, "\ncommitter ") + 11;
+	e = strchr(c, '\n');
+	if (!split_ident_line(&s, c, e - c))
+		*date = show_ident_date(&s, DATE_ISO8601);
+
+	c = strstr(c, "\n\n") + 2;
+	format_subject(&subject, c, " ");
+	*summary = strbuf_detach(&subject, NULL);
+}
+
+static void show_shortlog(const char *base, const char *head)
+{
+	struct commit *commit;
+	struct rev_info revs;
+	struct shortlog log;
+	const char *args[3];
+	struct strbuf tmp = STRBUF_INIT;
+
+	strbuf_addf(&tmp, "^%s", base);
+
+	args[1] = tmp.buf;
+	args[2] = head;
+
+	init_revisions(&revs, NULL);
+	setup_revisions(3, args, &revs, NULL);
+
+	strbuf_release(&tmp);
+
+	shortlog_init(&log);
+	prepare_revision_walk(&revs);
+	while ((commit = get_revision(&revs)))
+		shortlog_add_commit(&log, commit);
+	shortlog_output(&log);
+}
+
+static void show_diff(int patch, const unsigned char *base, const unsigned char *head)
+{
+	struct rev_info revs;
+	const char *args[3];
+	struct strbuf tmp = STRBUF_INIT;
+
+	strbuf_addf(&tmp, "^%s", sha1_to_hex(base));
+
+	args[1] = tmp.buf;
+	args[2] = sha1_to_hex(head);
+
+	init_revisions(&revs, NULL);
+	setup_revisions(3, args, &revs, NULL);
+	revs.diffopt.stat_width = -1;
+	revs.diffopt.stat_graph_width = -1;
+	revs.diffopt.output_format = patch ? DIFF_FORMAT_PATCH : DIFF_FORMAT_DIFFSTAT;
+	revs.diffopt.output_format |= DIFF_FORMAT_SUMMARY;
+	revs.diffopt.detect_rename = DIFF_DETECT_RENAME;
+	revs.diffopt.flags |= DIFF_OPT_RECURSIVE;
+
+	strbuf_release(&tmp);
+	diff_tree_sha1(base, head, "", &revs.diffopt);
+	log_tree_diff_flush(&revs);
+}
+
+int cmd_request_pull(int argc, const char **argv, const char *prefix)
+{
+	int patch;
+	const char *base, *url, *head;
+	char *head_ref;
+	char *branch_name = NULL;
+	struct strbuf branch_desc = STRBUF_INIT;
+	const char *ref;
+	int status = 0;
+	unsigned char head_id[20], base_id[20];
+	unsigned char *merge_base_id;
+	struct commit *base_commit, *head_commit, *merge_base_commit;
+	struct commit_list *merge_bases;
+	const char *merge_base_summary, *merge_base_date;
+	const char *head_summary, *head_date;
+	struct remote *remote;
+	struct transport *transport;
+
+	const struct option options[] = {
+		OPT_BOOL('p', NULL, &patch, N_("show patch text as well")),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, options, pull_request_usage, 0);
+
+	base = argv[0];
+	url = argv[1];
+	head = argv[2] ? argv[2] : "HEAD";
+	status = 0;
+
+	if (!base || !url)
+		usage_with_options(pull_request_usage, options);
+
+	if (dwim_ref(head, strlen(head), head_id, &head_ref) == 1) {
+		if (!prefixcmp(head_ref, "refs/heads/")) {
+			branch_name = head_ref + 11;
+			if (read_branch_desc(&branch_desc, branch_name) || !branch_desc.len)
+				branch_name = NULL;
+		}
+	}
+
+	describe(head);
+
+	get_sha1(base, base_id);
+	base_commit = lookup_commit_reference(base_id);
+	if (!base_commit)
+		die("Not a valid revision: %s", base);
+
+	head_commit = lookup_commit_reference(head_id);
+	if (!head_commit)
+		die("Not a valid revision: %s", head);
+
+	merge_bases = get_merge_bases(base_commit, head_commit, 0);
+	if (!merge_bases)
+		die("No commits in common between %s and %s", base, head);
+
+	merge_base_commit = merge_bases->item;
+	merge_base_id = merge_base_commit->object.sha1;
+
+	remote = remote_get(url);
+	url = remote->url[0];
+	transport = transport_get(remote, url);
+	ref = get_ref(transport, strcmp(head_ref, "HEAD") ? head_ref : NULL, head_id);
+	transport_disconnect(transport);
+
+	parse_buffer(merge_base_commit->buffer, &merge_base_summary, &merge_base_date);
+	parse_buffer(head_commit->buffer, &head_summary, &head_date);
+
+	printf("The following changes since commit %s:\n"
+			"\n"
+			"  %s (%s)\n"
+			"\n"
+			"are available in the git repository at:\n"
+			"\n",
+			sha1_to_hex(merge_base_id), merge_base_summary, merge_base_date);
+	printf("  %s", url);
+	if (ref)
+		printf(" %s", ref);
+	printf("\n");
+	printf("\n"
+			"for you to fetch changes up to %s:\n"
+			"\n"
+			"  %s (%s)\n"
+			"\n"
+			"----------------------------------------------------------------\n",
+			sha1_to_hex(head_id), head_summary, head_date);
+
+	if (branch_name)
+		printf("(from the branch description for %s local branch)\n\n%s\n", branch_name, branch_desc.buf);
+
+	if (tag_name) {
+		void *buffer;
+		enum object_type type;
+		unsigned long size;
+		unsigned char sha1[20];
+		const char *begin, *end;
+
+		if (!ref || prefixcmp(ref, "tags/") || strcmp(ref + 5, tag_name)) {
+			fprintf(stderr, "warn: You locally have %s but it does not (yet)\n", tag_name);
+			fprintf(stderr, "warn: appear to be at %s\n", url);
+			fprintf(stderr, "warn: Do you want to push it there, perhaps?\n");
+		}
+		get_sha1(tag_name, sha1);
+		buffer = read_sha1_file(sha1, &type, &size);
+		begin = strstr(buffer, "\n\n") + 2;
+		end = strstr(begin, "-----BEGIN PGP ");
+		if (!end)
+			end = begin + strlen(begin);
+		printf("%.*s\n", (int)(end - begin), begin);
+	}
+
+	if (branch_name || tag_name)
+		puts("----------------------------------------------------------------");
+
+	show_shortlog(base, head);
+	show_diff(patch, merge_base_id, head_id);
+
+	if (!ref) {
+		fprintf(stderr, "warn: No branch of %s is at:\n", url);
+		fprintf(stderr, "warn:   %s: %s\n", find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary);
+		fprintf(stderr, "warn: Are you sure you pushed '%s' there?\n", head);
+		status = 1;
+	}
+	return status;
+}
diff --git a/git-request-pull.rb b/git-request-pull.rb
deleted file mode 100755
index 941ff72..0000000
--- a/git-request-pull.rb
+++ /dev/null
@@ -1,199 +0,0 @@
-#!git ruby
-
-require 'date'
-
-patch = nil
-
-def usage
-  puts <<EOF
-usage: git request-pull [options] start url [end]
-
-    -p                    show patch text as well
-
-EOF
-  exit 1
-end
-
-def read_branch_desc(name)
-  git_config() do |key, value|
-    return value if key == "branch.#{name}.description"
-  end
-  return nil
-end
-
-def describe(rev)
-  for_each_ref() do |name, sha1, flags|
-    next unless name.start_with?('refs/tags/')
-    next unless peel_ref(name) == get_sha1(rev)
-    return name.skip_prefix('refs/tags/')
-  end
-  return nil
-end
-
-def abbr(ref)
-  if (ref =~ %r{^refs/heads/(.*)} || ref =~ %r{^refs/(tags/.*)})
-    return $1
-  end
-  return ref
-end
-
-# $head is the token given from the command line, and $tag_name, if
-# exists, is the tag we are going to show the commit information for.
-# If that tag exists at the remote and it points at the commit, use it.
-# Otherwise, if a branch with the same name as $head exists at the remote
-# and their values match, use that instead.
-#
-# Otherwise find a random ref that matches $head_id.
-
-def get_ref(transport, head_ref, head_id, tag_name)
-  found = nil
-  transport.get_remote_refs().each do |e|
-    sha1 = e.old_sha1
-    next unless sha1 == head_id
-    ref, deref = e.name.scan(/^(\S+?)(\^\{\})?$/).first
-    found = abbr(ref)
-    break if (deref && ref == "refs/tags/#{tag_name}")
-    break if ref == head_ref
-  end
-  return found
-end
-
-def parse_buffer(buffer)
-  summary = []
-  date = msg = nil
-  header, body = buffer.split("\n\n", 2)
-  header.each_line do |line|
-    case line
-    when /^committer ([^<>]+) <(\S+)> (.+)$/
-      date = DateTime.strptime($3, '%s %z')
-    end
-  end
-  body.each_line do |l|
-    break if (l.strip.empty?)
-    summary << l.chomp
-  end
-  summary = summary.join(' ')
-  date = date.strftime('%F %T %z')
-  return [summary, date]
-end
-
-def show_shortlog(base, head)
-  rev = Git::RevInfo.setup(nil, ['^' + base, head], nil)
-  shortlog(rev.to_a)
-end
-
-def show_diff(patch, base, head)
-  rev = Git::RevInfo.setup(nil, ['^' + sha1_to_hex(base), sha1_to_hex(head)], nil)
-  rev.diffopt.stat_width = -1
-  rev.diffopt.stat_graph_width = -1
-  rev.diffopt.output_format = patch ? DIFF_FORMAT_PATCH : DIFF_FORMAT_DIFFSTAT
-  rev.diffopt.output_format |= DIFF_FORMAT_SUMMARY
-  rev.diffopt.detect_rename = DIFF_DETECT_RENAME
-  rev.diffopt.flags |= DIFF_OPT_RECURSIVE
-
-  diff_tree_sha1(base, head, "", rev.diffopt)
-  log_tree_diff_flush(rev)
-end
-
-until ARGV.empty?
-  case ARGV.first
-  when '-p'
-    patch = '-p'
-  when '--'
-    ARGV.shift
-    break
-  when /^-/
-    usage
-  else
-    break
-  end
-  ARGV.shift
-end
-
-base = ARGV[0]
-url = ARGV[1]
-head = ARGV[2] || 'HEAD'
-branch_name = branch_desc = nil
-
-usage unless base or url
-
-_, _, head_ref = dwim_ref(head)
-
-if head_ref.start_with?('refs/heads')
-  branch_name = head_ref[11..-1]
-  branch_desc = read_branch_desc(branch_name)
-  branch_name = nil if not branch_desc
-end
-
-tag_name = describe(head)
-
-base_id = get_sha1("#{base}^0")
-die "Not a valid revision: #{base}" unless base_id
-
-head_id = get_sha1("#{head}^0")
-die "Not a valid revision: #{head}" unless head_id
-
-base_commit = Git::Commit.get(base_id)
-head_commit = Git::Commit.get(head_id)
-
-merge_bases = get_merge_bases([base_commit, head_commit], 0);
-die "No commits in common between #{base} and #{head}" unless merge_bases
-
-merge_base_id = merge_bases.first.sha1
-merge_base_commit = Git::Commit.get(merge_base_id)
-
-remote = remote_get(url)
-transport = transport_get(remote, nil)
-
-ref = get_ref(transport, head_ref != "HEAD" ? head_ref : nil, head_id, tag_name)
-url = remote.url.first
-
-merge_base_summary, merge_base_date = parse_buffer(merge_base_commit.buffer)
-head_summary, head_date = parse_buffer(head_commit.buffer)
-
-puts "The following changes since commit %s:
-
-  %s (%s)
-
-are available in the git repository at:
-
-" % [merge_base_commit, merge_base_summary, merge_base_date]
-puts "  #{url}" + (ref ? " #{ref}" : "")
-puts "
-for you to fetch changes up to %s:
-
-  %s (%s)
-
-----------------------------------------------------------------
-" % [head_commit, head_summary, head_date]
-
-if branch_name
-  puts "(from the branch description for #{branch_name} local branch)"
-  puts
-  puts branch_desc
-end
-
-if tag_name
-  if ref != "tags/#{tag_name}"
-    $stderr.puts "warn: You locally have #{tag_name} but it does not (yet)"
-    $stderr.puts "warn: appear to be at #{url}"
-    $stderr.puts "warn: Do you want to push it there, perhaps?"
-  end
-  buffer, _ = read_sha1_file(get_sha1(tag_name))
-  puts buffer.scan(/(?:\n\n)(.+)(?:-----BEGIN PGP )?/m).first
-  puts
-end
-
-if branch_name || tag_name
-  puts "----------------------------------------------------------------"
-end
-
-show_shortlog(base, head)
-show_diff(patch, merge_base_id, head_id)
-
-if ! ref
-  $stderr.puts "warn: No branch of #{url} is at:"
-  $stderr.puts "warn:   %s: %s'" % [find_unique_abbrev(head_id, DEFAULT_ABBREV), head_summary]
-  $stderr.puts "warn: Are you sure you pushed '#{abbr(head_ref)}' there?"
-  exit 1
-end
diff --git a/git.c b/git.c
index 2025f77..d67d3fb 100644
--- a/git.c
+++ b/git.c
@@ -398,6 +398,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "remote-fd", cmd_remote_fd },
 		{ "replace", cmd_replace, RUN_SETUP },
 		{ "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
+		{ "request-pull", cmd_request_pull, RUN_SETUP_GENTLY },
 		{ "rerere", cmd_rerere, RUN_SETUP },
 		{ "reset", cmd_reset, RUN_SETUP },
 		{ "rev-list", cmd_rev_list, RUN_SETUP },
-- 
1.8.4-fc

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]