Merge branch 'nd/clone-local-with-colon'
authorJunio C Hamano <gitster@pobox.com>
Sun, 2 Jun 2013 22:52:22 +0000 (15:52 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 2 Jun 2013 22:52:22 +0000 (15:52 -0700)
"git clone foo/bar:baz" cannot be a request to clone from a remote
over git-over-ssh specified in the scp style. Detect this case and
clone from a local repository at "foo/bar:baz".

* nd/clone-local-with-colon:
clone: allow cloning local paths with colons in them

1  2 
builtin/clone.c
connect.c
diff --combined builtin/clone.c
index 5e70696d8470a1a33066d291beb47dfb5164263a,a4d444a94b0166a0cd672b51ba4f2374b5d0038f..b6ffc6b4feb2dc56d8239cbfcea7d6467a652e6d
  #include "transport.h"
  #include "strbuf.h"
  #include "dir.h"
 -#include "pack-refs.h"
  #include "sigchain.h"
  #include "branch.h"
  #include "remote.h"
  #include "run-command.h"
 +#include "connected.h"
  
  /*
   * Overall FIXMEs:
@@@ -231,26 -231,16 +231,26 @@@ static void strip_trailing_slashes(cha
  static int add_one_reference(struct string_list_item *item, void *cb_data)
  {
        char *ref_git;
 +      const char *repo;
        struct strbuf alternate = STRBUF_INIT;
  
 -      /* Beware: real_path() and mkpath() return static buffer */
 +      /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */
        ref_git = xstrdup(real_path(item->string));
 -      if (is_directory(mkpath("%s/.git/objects", ref_git))) {
 +
 +      repo = read_gitfile(ref_git);
 +      if (!repo)
 +              repo = read_gitfile(mkpath("%s/.git", ref_git));
 +      if (repo) {
 +              free(ref_git);
 +              ref_git = xstrdup(repo);
 +      }
 +
 +      if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
                char *ref_git_git = mkpathdup("%s/.git", ref_git);
                free(ref_git);
                ref_git = ref_git_git;
        } else if (!is_directory(mkpath("%s/objects", ref_git)))
 -              die(_("reference repository '%s' is not a local directory."),
 +              die(_("reference repository '%s' is not a local repository."),
                    item->string);
  
        strbuf_addf(&alternate, "%s/objects", ref_git);
@@@ -386,32 -376,10 +386,32 @@@ static void clone_local(const char *src
  static const char *junk_work_tree;
  static const char *junk_git_dir;
  static pid_t junk_pid;
 +static enum {
 +      JUNK_LEAVE_NONE,
 +      JUNK_LEAVE_REPO,
 +      JUNK_LEAVE_ALL
 +} junk_mode = JUNK_LEAVE_NONE;
 +
 +static const char junk_leave_repo_msg[] =
 +N_("Clone succeeded, but checkout failed.\n"
 +   "You can inspect what was checked out with 'git status'\n"
 +   "and retry the checkout with 'git checkout -f HEAD'\n");
  
  static void remove_junk(void)
  {
        struct strbuf sb = STRBUF_INIT;
 +
 +      switch (junk_mode) {
 +      case JUNK_LEAVE_REPO:
 +              warning("%s", _(junk_leave_repo_msg));
 +              /* fall-through */
 +      case JUNK_LEAVE_ALL:
 +              return;
 +      default:
 +              /* proceed to removal */
 +              break;
 +      }
 +
        if (getpid() != junk_pid)
                return;
        if (junk_git_dir) {
@@@ -517,37 -485,12 +517,37 @@@ static void write_followtags(const stru
        }
  }
  
 +static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
 +{
 +      struct ref **rm = cb_data;
 +      struct ref *ref = *rm;
 +
 +      /*
 +       * Skip anything missing a peer_ref, which we are not
 +       * actually going to write a ref for.
 +       */
 +      while (ref && !ref->peer_ref)
 +              ref = ref->next;
 +      /* Returning -1 notes "end of list" to the caller. */
 +      if (!ref)
 +              return -1;
 +
 +      hashcpy(sha1, ref->old_sha1);
 +      *rm = ref->next;
 +      return 0;
 +}
 +
  static void update_remote_refs(const struct ref *refs,
                               const struct ref *mapped_refs,
                               const struct ref *remote_head_points_at,
                               const char *branch_top,
                               const char *msg)
  {
 +      const struct ref *rm = mapped_refs;
 +
 +      if (check_everything_connected(iterate_ref_map, 0, &rm))
 +              die(_("remote did not send all necessary objects"));
 +
        if (refs) {
                write_remote_refs(mapped_refs);
                if (option_single_branch)
@@@ -636,8 -579,7 +636,8 @@@ static int checkout(void
        tree = parse_tree_indirect(sha1);
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
 -      unpack_trees(1, &t, &opts);
 +      if (unpack_trees(1, &t, &opts) < 0)
 +              die(_("unable to checkout working tree"));
  
        if (write_cache(fd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
@@@ -782,6 -724,8 +782,8 @@@ int cmd_clone(int argc, const char **ar
        is_local = option_local != 0 && path && !is_bundle;
        if (is_local && option_depth)
                warning(_("--depth is ignored in local clones; use file:// instead."));
+       if (option_local > 0 && !is_local)
+               warning(_("--local is ignored"));
  
        if (argc == 2)
                dir = xstrdup(argv[1]);
        transport_unlock_pack(transport);
        transport_disconnect(transport);
  
 +      junk_mode = JUNK_LEAVE_REPO;
        err = checkout();
  
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
        strbuf_release(&key);
        strbuf_release(&value);
 -      junk_pid = 0;
 +      junk_mode = JUNK_LEAVE_ALL;
        return err;
  }
diff --combined connect.c
index f57efd06c1d7ab01076b67d37ed24e34e17c4ebb,715a309d2152b5d8ba5624b38657a714adaf67b5..a0783d4867c5e6a9496e11ed043f666a402c5db9
+++ b/connect.c
@@@ -62,8 -62,8 +62,8 @@@ static void die_initial_contact(int got
  /*
   * Read all the refs from the other end
   */
 -struct ref **get_remote_heads(int in, struct ref **list,
 -                            unsigned int flags,
 +struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 +                            struct ref **list, unsigned int flags,
                              struct extra_have_objects *extra_have)
  {
        int got_at_least_one_head = 0;
        for (;;) {
                struct ref *ref;
                unsigned char old_sha1[20];
 -              static char buffer[1000];
                char *name;
                int len, name_len;
 +              char *buffer = packet_buffer;
  
 -              len = packet_read(in, buffer, sizeof(buffer));
 +              len = packet_read(in, &src_buf, &src_len,
 +                                packet_buffer, sizeof(packet_buffer),
 +                                PACKET_READ_GENTLE_ON_EOF |
 +                                PACKET_READ_CHOMP_NEWLINE);
                if (len < 0)
                        die_initial_contact(got_at_least_one_head);
  
                if (!len)
                        break;
 -              if (buffer[len-1] == '\n')
 -                      buffer[--len] = 0;
  
                if (len > 4 && !prefixcmp(buffer, "ERR "))
                        die("remote error: %s", buffer + 4);
@@@ -551,8 -550,11 +551,11 @@@ struct child_process *git_connect(int f
        path = strchr(end, c);
        if (path && !has_dos_drive_prefix(end)) {
                if (c == ':') {
-                       protocol = PROTO_SSH;
-                       *path++ = '\0';
+                       if (path < strchrnul(host, '/')) {
+                               protocol = PROTO_SSH;
+                               *path++ = '\0';
+                       } else /* '/' in the host part, assume local path */
+                               path = end;
                }
        } else
                path = end;