Signed-off-by: Felipe Contreras <felipe.contre...@gmail.com>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to