Merge git://bogomips.org/git-svn
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 May 2012 20:47:55 +0000 (13:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 May 2012 20:47:55 +0000 (13:47 -0700)
By Jonathan Nieder
via Eric Wong
* git://bogomips.org/git-svn:
git-svn: make Git::SVN::Fetcher a separate file
git-svn: rename SVN::Git::* packages to Git::SVN::*
git-svn: move Git::SVN::Prompt into its own file

27 files changed:
Documentation/git-commit-tree.txt
Documentation/git-var.txt
builtin/commit.c
builtin/fetch-pack.c
builtin/fmt-merge-msg.c
builtin/log.c
builtin/merge.c
builtin/pack-objects.c
builtin/tag.c
builtin/var.c
cache.h
commit.c
config.c
environment.c
git-compat-util.h
gpg-interface.c
http-push.c
ident.c
log-tree.c
log-tree.h
refs.c
remote.c
remote.h
streaming.c
t/t5701-clone-local.sh
t/t9801-git-p4-branch.sh
wrapper.c
index cfb9906bb52f5bd81301e4859759b17ab124cada..eb8ee9999eebce4baf5008e604acfee4ccda6bd8 100644 (file)
@@ -88,15 +88,6 @@ for one to be entered and terminated with ^D.
 
 include::date-formats.txt[]
 
-Diagnostics
------------
-You don't exist. Go away!::
-    The passwd(5) gecos field couldn't be read
-Your parents must have hated you!::
-    The passwd(5) gecos field is longer than a giant static buffer.
-Your sysadmin must hate you!::
-    The passwd(5) name field is longer than a giant static buffer.
-
 Discussion
 ----------
 
index 988a3234f435ccd55d20f9d0e48c146b55691167..67edf586896a37818de13c959fcc23479eb9bef2 100644 (file)
@@ -59,15 +59,6 @@ ifdef::git-default-pager[]
     The build you are using chose '{git-default-pager}' as the default.
 endif::git-default-pager[]
 
-Diagnostics
------------
-You don't exist. Go away!::
-    The passwd(5) gecos field couldn't be read
-Your parents must have hated you!::
-    The passwd(5) gecos field is longer than a giant static buffer.
-Your sysadmin must hate you!::
-    The passwd(5) name field is longer than a giant static buffer.
-
 SEE ALSO
 --------
 linkgit:git-commit-tree[1]
index a2ec73d7382b62b182e344baeef9e26488187818..f43eaafb3b0afc1fb2dcc994433a6ad3be725654 100644 (file)
@@ -526,8 +526,7 @@ static void determine_author_info(struct strbuf *author_ident)
 
        if (force_date)
                date = force_date;
-       strbuf_addstr(author_ident, fmt_ident(name, email, date,
-                                             IDENT_ERROR_ON_NO_NAME));
+       strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
        if (!split_ident_line(&author, author_ident->buf, author_ident->len)) {
                export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
                export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
index 10db15b18403f01b9ee5db27b37d1e0cdbb2abb1..149db88726b8b5f924e6d136828008ec952bc88e 100644 (file)
@@ -528,6 +528,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
        struct ref **newtail = &newlist;
        struct ref *ref, *next;
        struct ref *fastarray[32];
+       int match_pos;
 
        if (nr_match && !args.fetch_all) {
                if (ARRAY_SIZE(fastarray) < nr_match)
@@ -540,6 +541,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
        else
                return_refs = NULL;
 
+       match_pos = 0;
        for (ref = *refs; ref; ref = next) {
                next = ref->next;
                if (!memcmp(ref->name, "refs/", 5) &&
@@ -553,15 +555,20 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
                        continue;
                }
                else {
-                       int i;
-                       for (i = 0; i < nr_match; i++) {
-                               if (!strcmp(ref->name, match[i])) {
-                                       match[i][0] = '\0';
-                                       return_refs[i] = ref;
+                       int cmp = -1;
+                       while (match_pos < nr_match) {
+                               cmp = strcmp(ref->name, match[match_pos]);
+                               if (cmp < 0) /* definitely do not have it */
+                                       break;
+                               else if (cmp == 0) { /* definitely have it */
+                                       match[match_pos][0] = '\0';
+                                       return_refs[match_pos] = ref;
                                        break;
                                }
+                               else /* might have it; keep looking */
+                                       match_pos++;
                        }
-                       if (i < nr_match)
+                       if (!cmp)
                                continue; /* we will link it later */
                }
                free(ref);
@@ -777,6 +784,8 @@ static struct ref *do_fetch_pack(int fd[2],
        struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
 
+       sort_ref_list(&ref, ref_compare_name);
+
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
        if (server_supports("multi_ack_detailed")) {
@@ -834,21 +843,12 @@ static int remove_duplicates(int nr_heads, char **heads)
 {
        int src, dst;
 
-       for (src = dst = 0; src < nr_heads; src++) {
-               /* If heads[src] is different from any of
-                * heads[0..dst], push it in.
-                */
-               int i;
-               for (i = 0; i < dst; i++) {
-                       if (!strcmp(heads[i], heads[src]))
-                               break;
-               }
-               if (i < dst)
-                       continue;
-               if (src != dst)
-                       heads[dst] = heads[src];
-               dst++;
-       }
+       if (!nr_heads)
+               return 0;
+
+       for (src = dst = 1; src < nr_heads; src++)
+               if (strcmp(heads[src], heads[dst-1]))
+                       heads[dst++] = heads[src];
        return dst;
 }
 
@@ -899,9 +899,11 @@ static void fetch_pack_setup(void)
 
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 {
-       int i, ret, nr_heads;
+       int i, ret;
        struct ref *ref = NULL;
-       char *dest = NULL, **heads;
+       const char *dest = NULL;
+       int alloc_heads = 0, nr_heads = 0;
+       char **heads = NULL;
        int fd[2];
        char *pack_lockfile = NULL;
        char **pack_lockfile_ptr = NULL;
@@ -909,84 +911,79 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 
        packet_trace_identity("fetch-pack");
 
-       nr_heads = 0;
-       heads = NULL;
-       for (i = 1; i < argc; i++) {
+       for (i = 1; i < argc && *argv[i] == '-'; i++) {
                const char *arg = argv[i];
 
-               if (*arg == '-') {
-                       if (!prefixcmp(arg, "--upload-pack=")) {
-                               args.uploadpack = arg + 14;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--exec=")) {
-                               args.uploadpack = arg + 7;
-                               continue;
-                       }
-                       if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
-                               args.quiet = 1;
-                               continue;
-                       }
-                       if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
-                               args.lock_pack = args.keep_pack;
-                               args.keep_pack = 1;
-                               continue;
-                       }
-                       if (!strcmp("--thin", arg)) {
-                               args.use_thin_pack = 1;
-                               continue;
-                       }
-                       if (!strcmp("--include-tag", arg)) {
-                               args.include_tag = 1;
-                               continue;
-                       }
-                       if (!strcmp("--all", arg)) {
-                               args.fetch_all = 1;
-                               continue;
-                       }
-                       if (!strcmp("--stdin", arg)) {
-                               args.stdin_refs = 1;
-                               continue;
-                       }
-                       if (!strcmp("-v", arg)) {
-                               args.verbose = 1;
-                               continue;
-                       }
-                       if (!prefixcmp(arg, "--depth=")) {
-                               args.depth = strtol(arg + 8, NULL, 0);
-                               continue;
-                       }
-                       if (!strcmp("--no-progress", arg)) {
-                               args.no_progress = 1;
-                               continue;
-                       }
-                       if (!strcmp("--stateless-rpc", arg)) {
-                               args.stateless_rpc = 1;
-                               continue;
-                       }
-                       if (!strcmp("--lock-pack", arg)) {
-                               args.lock_pack = 1;
-                               pack_lockfile_ptr = &pack_lockfile;
-                               continue;
-                       }
-                       usage(fetch_pack_usage);
+               if (!prefixcmp(arg, "--upload-pack=")) {
+                       args.uploadpack = arg + 14;
+                       continue;
                }
-               dest = (char *)arg;
-               heads = (char **)(argv + i + 1);
-               nr_heads = argc - i - 1;
-               break;
+               if (!prefixcmp(arg, "--exec=")) {
+                       args.uploadpack = arg + 7;
+                       continue;
+               }
+               if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+                       args.quiet = 1;
+                       continue;
+               }
+               if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
+                       args.lock_pack = args.keep_pack;
+                       args.keep_pack = 1;
+                       continue;
+               }
+               if (!strcmp("--thin", arg)) {
+                       args.use_thin_pack = 1;
+                       continue;
+               }
+               if (!strcmp("--include-tag", arg)) {
+                       args.include_tag = 1;
+                       continue;
+               }
+               if (!strcmp("--all", arg)) {
+                       args.fetch_all = 1;
+                       continue;
+               }
+               if (!strcmp("--stdin", arg)) {
+                       args.stdin_refs = 1;
+                       continue;
+               }
+               if (!strcmp("-v", arg)) {
+                       args.verbose = 1;
+                       continue;
+               }
+               if (!prefixcmp(arg, "--depth=")) {
+                       args.depth = strtol(arg + 8, NULL, 0);
+                       continue;
+               }
+               if (!strcmp("--no-progress", arg)) {
+                       args.no_progress = 1;
+                       continue;
+               }
+               if (!strcmp("--stateless-rpc", arg)) {
+                       args.stateless_rpc = 1;
+                       continue;
+               }
+               if (!strcmp("--lock-pack", arg)) {
+                       args.lock_pack = 1;
+                       pack_lockfile_ptr = &pack_lockfile;
+                       continue;
+               }
+               usage(fetch_pack_usage);
        }
-       if (!dest)
+
+       if (i < argc)
+               dest = argv[i++];
+       else
                usage(fetch_pack_usage);
 
+       /*
+        * Copy refs from cmdline to growable list, then append any
+        * refs from the standard input:
+        */
+       ALLOC_GROW(heads, argc - i, alloc_heads);
+       for (; i < argc; i++)
+               heads[nr_heads++] = xstrdup(argv[i]);
        if (args.stdin_refs) {
-               /*
-                * Copy refs from cmdline to new growable list, then
-                * append the refs from the standard input.
-                */
-               int alloc_heads = nr_heads;
-               int size = nr_heads * sizeof(*heads);
-               heads = memcpy(xmalloc(size), heads, size);
                if (args.stateless_rpc) {
                        /* in stateless RPC mode we use pkt-line to read
                         * from stdin, until we get a flush packet
@@ -1018,7 +1015,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                fd[0] = 0;
                fd[1] = 1;
        } else {
-               conn = git_connect(fd, (char *)dest, args.uploadpack,
+               conn = git_connect(fd, dest, args.uploadpack,
                                   args.verbose ? CONNECT_VERBOSE : 0);
        }
 
@@ -1057,6 +1054,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        return ret;
 }
 
+static int compare_heads(const void *a, const void *b)
+{
+       return strcmp(*(const char **)a, *(const char **)b);
+}
+
 struct ref *fetch_pack(struct fetch_pack_args *my_args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
@@ -1076,8 +1078,11 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        st.st_mtime = 0;
        }
 
-       if (heads && nr_heads)
+       if (heads && nr_heads) {
+               qsort(heads, nr_heads, sizeof(*heads), compare_heads);
                nr_heads = remove_duplicates(nr_heads, heads);
+       }
+
        if (!ref) {
                packet_flush(fd[1]);
                die("no matching remote head");
index d42015d8672d7a929758f69371eacbbc59c92888..bf93b043b79a0a3001d427f78009a1cdd26db8bb 100644 (file)
@@ -230,7 +230,7 @@ static void add_branch_desc(struct strbuf *out, const char *name)
 static void record_person(int which, struct string_list *people,
                          struct commit *commit)
 {
-       char name_buf[MAX_GITNAME], *name, *name_end;
+       char *name_buf, *name, *name_end;
        struct string_list_item *elem;
        const char *field = (which == 'a') ? "\nauthor " : "\ncommitter ";
 
@@ -243,10 +243,9 @@ static void record_person(int which, struct string_list *people,
                name_end--;
        while (isspace(*name_end) && name <= name_end)
                name_end--;
-       if (name_end < name || name + MAX_GITNAME <= name_end)
+       if (name_end < name)
                return;
-       memcpy(name_buf, name, name_end - name + 1);
-       name_buf[name_end - name + 1] = '\0';
+       name_buf = xmemdupz(name, name_end - name + 1);
 
        elem = string_list_lookup(people, name_buf);
        if (!elem) {
@@ -254,6 +253,7 @@ static void record_person(int which, struct string_list *people,
                elem->util = (void *)0;
        }
        elem->util = (void*)(util_as_integral(elem) + 1);
+       free(name_buf);
 }
 
 static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
index 690caa7830b2a4549012db5e46794118bc36e989..906dca475af0ed51d499cce4a7c0a34f038c5810 100644 (file)
@@ -663,7 +663,8 @@ static FILE *realstdout = NULL;
 static const char *output_directory = NULL;
 static int outdir_offset;
 
-static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet)
+static int reopen_stdout(struct commit *commit, const char *subject,
+                        struct rev_info *rev, int quiet)
 {
        struct strbuf filename = STRBUF_INIT;
        int suffix_len = strlen(fmt_patch_suffix) + 1;
@@ -677,7 +678,7 @@ static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet)
                        strbuf_addch(&filename, '/');
        }
 
-       get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
+       get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename);
 
        if (!quiet)
                fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
@@ -737,15 +738,10 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
 
 static void gen_message_id(struct rev_info *info, char *base)
 {
-       const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
-       const char *email_start = strrchr(committer, '<');
-       const char *email_end = strrchr(committer, '>');
        struct strbuf buf = STRBUF_INIT;
-       if (!email_start || !email_end || email_start > email_end - 1)
-               die(_("Could not extract email from committer identity."));
-       strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
+       strbuf_addf(&buf, "%s.%lu.git.%s", base,
                    (unsigned long) time(NULL),
-                   (int)(email_end - email_start - 1), email_start + 1);
+                   git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
        info->message_id = strbuf_detach(&buf, NULL);
 }
 
@@ -784,7 +780,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        const char *encoding = "UTF-8";
        struct diff_options opts;
        int need_8bit_cte = 0;
-       struct commit *commit = NULL;
        struct pretty_print_context pp = {0};
 
        if (rev->commit_format != CMIT_FMT_EMAIL)
@@ -792,31 +787,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
 
        committer = git_committer_info(0);
 
-       if (!numbered_files) {
-               /*
-                * We fake a commit for the cover letter so we get the filename
-                * desired.
-                */
-               commit = xcalloc(1, sizeof(*commit));
-               commit->buffer = xmalloc(400);
-               snprintf(commit->buffer, 400,
-                       "tree 0000000000000000000000000000000000000000\n"
-                       "parent %s\n"
-                       "author %s\n"
-                       "committer %s\n\n"
-                       "cover letter\n",
-                       sha1_to_hex(head->object.sha1), committer, committer);
-       }
-
-       if (!use_stdout && reopen_stdout(commit, rev, quiet))
+       if (!use_stdout &&
+           reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
 
-       if (commit) {
-
-               free(commit->buffer);
-               free(commit);
-       }
-
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
 
@@ -1173,7 +1147,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (do_signoff) {
                const char *committer;
                const char *endpos;
-               committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
+               committer = git_committer_info(IDENT_STRICT);
                endpos = strchr(committer, '>');
                if (!endpos)
                        die(_("bogus committer info %s"), committer);
@@ -1411,8 +1385,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
 
-               if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
-                                                &rev, quiet))
+               if (!use_stdout &&
+                   reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
index 470fc57c5d6ceabedbd866a5db35d3123a764af0..dd50a0c57b4d73d03cc84afd90594a56616d4953 100644 (file)
@@ -1447,7 +1447,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                refresh_cache(REFRESH_QUIET);
                if (allow_trivial && !fast_forward_only) {
                        /* See if it is really trivial. */
-                       git_committer_info(IDENT_ERROR_ON_NO_NAME);
+                       git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
                        if (!read_tree_trivial(common->item->object.sha1,
                                               head_commit->object.sha1,
@@ -1490,7 +1490,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                die(_("Not possible to fast-forward, aborting."));
 
        /* We are going to make a new commit. */
-       git_committer_info(IDENT_ERROR_ON_NO_NAME);
+       git_committer_info(IDENT_STRICT);
 
        /*
         * At this point, we need a real merge.  No matter what strategy
index 1861093e9db4b951e4cb4c790d4792fe7bc70da7..ccfcbad14647eff80131cd99c22a18f13016b4af 100644 (file)
@@ -200,22 +200,178 @@ static void copy_pack_data(struct sha1file *f,
 }
 
 /* Return 0 if we will bust the pack-size limit */
-static unsigned long write_object(struct sha1file *f,
-                                 struct object_entry *entry,
-                                 off_t write_offset)
+static unsigned long write_no_reuse_object(struct sha1file *f, struct object_entry *entry,
+                                          unsigned long limit, int usable_delta)
 {
-       unsigned long size, limit, datalen;
-       void *buf;
+       unsigned long size, datalen;
        unsigned char header[10], dheader[10];
        unsigned hdrlen;
        enum object_type type;
+       void *buf;
+
+       if (!usable_delta) {
+               buf = read_sha1_file(entry->idx.sha1, &type, &size);
+               if (!buf)
+                       die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+               /*
+                * make sure no cached delta data remains from a
+                * previous attempt before a pack split occurred.
+                */
+               free(entry->delta_data);
+               entry->delta_data = NULL;
+               entry->z_delta_size = 0;
+       } else if (entry->delta_data) {
+               size = entry->delta_size;
+               buf = entry->delta_data;
+               entry->delta_data = NULL;
+               type = (allow_ofs_delta && entry->delta->idx.offset) ?
+                       OBJ_OFS_DELTA : OBJ_REF_DELTA;
+       } else {
+               buf = get_delta(entry);
+               size = entry->delta_size;
+               type = (allow_ofs_delta && entry->delta->idx.offset) ?
+                       OBJ_OFS_DELTA : OBJ_REF_DELTA;
+       }
+
+       if (entry->z_delta_size)
+               datalen = entry->z_delta_size;
+       else
+               datalen = do_compress(&buf, size);
+
+       /*
+        * The object header is a byte of 'type' followed by zero or
+        * more bytes of length.
+        */
+       hdrlen = encode_in_pack_object_header(type, size, header);
+
+       if (type == OBJ_OFS_DELTA) {
+               /*
+                * Deltas with relative base contain an additional
+                * encoding of the relative offset for the delta
+                * base from this object's position in the pack.
+                */
+               off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+               unsigned pos = sizeof(dheader) - 1;
+               dheader[pos] = ofs & 127;
+               while (ofs >>= 7)
+                       dheader[--pos] = 128 | (--ofs & 127);
+               if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+                       free(buf);
+                       return 0;
+               }
+               sha1write(f, header, hdrlen);
+               sha1write(f, dheader + pos, sizeof(dheader) - pos);
+               hdrlen += sizeof(dheader) - pos;
+       } else if (type == OBJ_REF_DELTA) {
+               /*
+                * Deltas with a base reference contain
+                * an additional 20 bytes for the base sha1.
+                */
+               if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+                       free(buf);
+                       return 0;
+               }
+               sha1write(f, header, hdrlen);
+               sha1write(f, entry->delta->idx.sha1, 20);
+               hdrlen += 20;
+       } else {
+               if (limit && hdrlen + datalen + 20 >= limit) {
+                       free(buf);
+                       return 0;
+               }
+               sha1write(f, header, hdrlen);
+       }
+       sha1write(f, buf, datalen);
+       free(buf);
+
+       return hdrlen + datalen;
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry,
+                                       unsigned long limit, int usable_delta)
+{
+       struct packed_git *p = entry->in_pack;
+       struct pack_window *w_curs = NULL;
+       struct revindex_entry *revidx;
+       off_t offset;
+       enum object_type type = entry->type;
+       unsigned long datalen;
+       unsigned char header[10], dheader[10];
+       unsigned hdrlen;
+
+       if (entry->delta)
+               type = (allow_ofs_delta && entry->delta->idx.offset) ?
+                       OBJ_OFS_DELTA : OBJ_REF_DELTA;
+       hdrlen = encode_in_pack_object_header(type, entry->size, header);
+
+       offset = entry->in_pack_offset;
+       revidx = find_pack_revindex(p, offset);
+       datalen = revidx[1].offset - offset;
+       if (!pack_to_stdout && p->index_version > 1 &&
+           check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
+               error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+               unuse_pack(&w_curs);
+               return write_no_reuse_object(f, entry, limit, usable_delta);
+       }
+
+       offset += entry->in_pack_header_size;
+       datalen -= entry->in_pack_header_size;
+
+       if (!pack_to_stdout && p->index_version == 1 &&
+           check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
+               error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
+               unuse_pack(&w_curs);
+               return write_no_reuse_object(f, entry, limit, usable_delta);
+       }
+
+       if (type == OBJ_OFS_DELTA) {
+               off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+               unsigned pos = sizeof(dheader) - 1;
+               dheader[pos] = ofs & 127;
+               while (ofs >>= 7)
+                       dheader[--pos] = 128 | (--ofs & 127);
+               if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+                       unuse_pack(&w_curs);
+                       return 0;
+               }
+               sha1write(f, header, hdrlen);
+               sha1write(f, dheader + pos, sizeof(dheader) - pos);
+               hdrlen += sizeof(dheader) - pos;
+               reused_delta++;
+       } else if (type == OBJ_REF_DELTA) {
+               if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+                       unuse_pack(&w_curs);
+                       return 0;
+               }
+               sha1write(f, header, hdrlen);
+               sha1write(f, entry->delta->idx.sha1, 20);
+               hdrlen += 20;
+               reused_delta++;
+       } else {
+               if (limit && hdrlen + datalen + 20 >= limit) {
+                       unuse_pack(&w_curs);
+                       return 0;
+               }
+               sha1write(f, header, hdrlen);
+       }
+       copy_pack_data(f, p, &w_curs, offset, datalen);
+       unuse_pack(&w_curs);
+       reused++;
+       return hdrlen + datalen;
+}
+
+/* Return 0 if we will bust the pack-size limit */
+static unsigned long write_object(struct sha1file *f,
+                                 struct object_entry *entry,
+                                 off_t write_offset)
+{
+       unsigned long limit, len;
        int usable_delta, to_reuse;
 
        if (!pack_to_stdout)
                crc32_begin(f);
 
-       type = entry->type;
-
        /* apply size limit if limited packsize and not first object */
        if (!pack_size_limit || !nr_written)
                limit = 0;
@@ -243,11 +399,11 @@ static unsigned long write_object(struct sha1file *f,
                to_reuse = 0;   /* explicit */
        else if (!entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
-       else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA)
+       else if (entry->type == OBJ_REF_DELTA || entry->type == OBJ_OFS_DELTA)
                                /* check_object() decided it for us ... */
                to_reuse = usable_delta;
                                /* ... but pack split may override that */
-       else if (type != entry->in_pack_type)
+       else if (entry->type != entry->in_pack_type)
                to_reuse = 0;   /* pack has delta which is unusable */
        else if (entry->delta)
                to_reuse = 0;   /* we want to pack afresh */
@@ -256,153 +412,19 @@ static unsigned long write_object(struct sha1file *f,
                                 * and we do not need to deltify it.
                                 */
 
-       if (!to_reuse) {
-               no_reuse:
-               if (!usable_delta) {
-                       buf = read_sha1_file(entry->idx.sha1, &type, &size);
-                       if (!buf)
-                               die("unable to read %s", sha1_to_hex(entry->idx.sha1));
-                       /*
-                        * make sure no cached delta data remains from a
-                        * previous attempt before a pack split occurred.
-                        */
-                       free(entry->delta_data);
-                       entry->delta_data = NULL;
-                       entry->z_delta_size = 0;
-               } else if (entry->delta_data) {
-                       size = entry->delta_size;
-                       buf = entry->delta_data;
-                       entry->delta_data = NULL;
-                       type = (allow_ofs_delta && entry->delta->idx.offset) ?
-                               OBJ_OFS_DELTA : OBJ_REF_DELTA;
-               } else {
-                       buf = get_delta(entry);
-                       size = entry->delta_size;
-                       type = (allow_ofs_delta && entry->delta->idx.offset) ?
-                               OBJ_OFS_DELTA : OBJ_REF_DELTA;
-               }
-
-               if (entry->z_delta_size)
-                       datalen = entry->z_delta_size;
-               else
-                       datalen = do_compress(&buf, size);
-
-               /*
-                * The object header is a byte of 'type' followed by zero or
-                * more bytes of length.
-                */
-               hdrlen = encode_in_pack_object_header(type, size, header);
-
-               if (type == OBJ_OFS_DELTA) {
-                       /*
-                        * Deltas with relative base contain an additional
-                        * encoding of the relative offset for the delta
-                        * base from this object's position in the pack.
-                        */
-                       off_t ofs = entry->idx.offset - entry->delta->idx.offset;
-                       unsigned pos = sizeof(dheader) - 1;
-                       dheader[pos] = ofs & 127;
-                       while (ofs >>= 7)
-                               dheader[--pos] = 128 | (--ofs & 127);
-                       if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
-                               free(buf);
-                               return 0;
-                       }
-                       sha1write(f, header, hdrlen);
-                       sha1write(f, dheader + pos, sizeof(dheader) - pos);
-                       hdrlen += sizeof(dheader) - pos;
-               } else if (type == OBJ_REF_DELTA) {
-                       /*
-                        * Deltas with a base reference contain
-                        * an additional 20 bytes for the base sha1.
-                        */
-                       if (limit && hdrlen + 20 + datalen + 20 >= limit) {
-                               free(buf);
-                               return 0;
-                       }
-                       sha1write(f, header, hdrlen);
-                       sha1write(f, entry->delta->idx.sha1, 20);
-                       hdrlen += 20;
-               } else {
-                       if (limit && hdrlen + datalen + 20 >= limit) {
-                               free(buf);
-                               return 0;
-                       }
-                       sha1write(f, header, hdrlen);
-               }
-               sha1write(f, buf, datalen);
-               free(buf);
-       }
-       else {
-               struct packed_git *p = entry->in_pack;
-               struct pack_window *w_curs = NULL;
-               struct revindex_entry *revidx;
-               off_t offset;
-
-               if (entry->delta)
-                       type = (allow_ofs_delta && entry->delta->idx.offset) ?
-                               OBJ_OFS_DELTA : OBJ_REF_DELTA;
-               hdrlen = encode_in_pack_object_header(type, entry->size, header);
-
-               offset = entry->in_pack_offset;
-               revidx = find_pack_revindex(p, offset);
-               datalen = revidx[1].offset - offset;
-               if (!pack_to_stdout && p->index_version > 1 &&
-                   check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
-                       error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
-                       unuse_pack(&w_curs);
-                       goto no_reuse;
-               }
-
-               offset += entry->in_pack_header_size;
-               datalen -= entry->in_pack_header_size;
-               if (!pack_to_stdout && p->index_version == 1 &&
-                   check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
-                       error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
-                       unuse_pack(&w_curs);
-                       goto no_reuse;
-               }
+       if (!to_reuse)
+               len = write_no_reuse_object(f, entry, limit, usable_delta);
+       else
+               len = write_reuse_object(f, entry, limit, usable_delta);
+       if (!len)
+               return 0;
 
-               if (type == OBJ_OFS_DELTA) {
-                       off_t ofs = entry->idx.offset - entry->delta->idx.offset;
-                       unsigned pos = sizeof(dheader) - 1;
-                       dheader[pos] = ofs & 127;
-                       while (ofs >>= 7)
-                               dheader[--pos] = 128 | (--ofs & 127);
-                       if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
-                               unuse_pack(&w_curs);
-                               return 0;
-                       }
-                       sha1write(f, header, hdrlen);
-                       sha1write(f, dheader + pos, sizeof(dheader) - pos);
-                       hdrlen += sizeof(dheader) - pos;
-                       reused_delta++;
-               } else if (type == OBJ_REF_DELTA) {
-                       if (limit && hdrlen + 20 + datalen + 20 >= limit) {
-                               unuse_pack(&w_curs);
-                               return 0;
-                       }
-                       sha1write(f, header, hdrlen);
-                       sha1write(f, entry->delta->idx.sha1, 20);
-                       hdrlen += 20;
-                       reused_delta++;
-               } else {
-                       if (limit && hdrlen + datalen + 20 >= limit) {
-                               unuse_pack(&w_curs);
-                               return 0;
-                       }
-                       sha1write(f, header, hdrlen);
-               }
-               copy_pack_data(f, p, &w_curs, offset, datalen);
-               unuse_pack(&w_curs);
-               reused++;
-       }
        if (usable_delta)
                written_delta++;
        written++;
        if (!pack_to_stdout)
                entry->idx.crc32 = crc32_end(f);
-       return hdrlen + datalen;
+       return len;
 }
 
 enum write_one_status {
@@ -1327,7 +1349,7 @@ static void get_object_details(void)
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *entry = sorted_by_offset[i];
                check_object(entry);
-               if (big_file_threshold <= entry->size)
+               if (big_file_threshold < entry->size)
                        entry->no_try_delta = 1;
        }
 
index 4fb6bd7b3dd39dadbbb2eb3bfb8a8c3dc96e8484..7b1be85e48b67f2a2eb075f6d8366246c7938646 100644 (file)
@@ -332,7 +332,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                          sha1_to_hex(object),
                          typename(type),
                          tag,
-                         git_committer_info(IDENT_ERROR_ON_NO_NAME));
+                         git_committer_info(IDENT_STRICT));
 
        if (header_len > sizeof(header_buf) - 1)
                die(_("tag header too big."));
index 99d068a5327255f36c8e415ecb8d0cf98cbbe180..aedbb53a2da5e02b2c8fc29a1a4dc22dc76fbefb 100644 (file)
@@ -11,7 +11,7 @@ static const char *editor(int flag)
 {
        const char *pgm = git_editor();
 
-       if (!pgm && flag & IDENT_ERROR_ON_NO_NAME)
+       if (!pgm && flag & IDENT_STRICT)
                die("Terminal is dumb, but EDITOR unset");
 
        return pgm;
@@ -55,7 +55,7 @@ static const char *read_var(const char *var)
        val = NULL;
        for (ptr = git_vars; ptr->read; ptr++) {
                if (strcmp(var, ptr->name) == 0) {
-                       val = ptr->read(IDENT_ERROR_ON_NO_NAME);
+                       val = ptr->read(IDENT_STRICT);
                        break;
                }
        }
diff --git a/cache.h b/cache.h
index cc5048c202b6799591e7121de91ee2c6cdce9997..06413e1584dc762063feaa3728209a4d2864ebc7 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -887,15 +887,19 @@ unsigned long approxidate_careful(const char *, int *);
 unsigned long approxidate_relative(const char *date, const struct timeval *now);
 enum date_mode parse_date_format(const char *format);
 
-#define IDENT_WARN_ON_NO_NAME  1
-#define IDENT_ERROR_ON_NO_NAME 2
-#define IDENT_NO_DATE         4
+#define IDENT_STRICT          1
+#define IDENT_NO_DATE         2
+#define IDENT_NO_NAME         4
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
+extern const char *ident_default_name(void);
+extern const char *ident_default_email(void);
+extern const char *ident_default_date(void);
 extern const char *git_editor(void);
 extern const char *git_pager(int stdout_is_tty);
+extern int git_ident_config(const char *, const char *, void *);
 
 struct ident_split {
        const char *name_begin;
@@ -1139,9 +1143,6 @@ struct config_include_data {
 #define CONFIG_INCLUDE_INIT { 0 }
 extern int git_config_include(const char *name, const char *value, void *data);
 
-#define MAX_GITNAME (1000)
-extern char git_default_email[MAX_GITNAME];
-extern char git_default_name[MAX_GITNAME];
 #define IDENT_NAME_GIVEN 01
 #define IDENT_MAIL_GIVEN 02
 #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
index 9ed36c7db5ee0d2e820a01b27507c4ab0afe9663..8248a994a50ab91874600567e544aa3735e4aa98 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1154,9 +1154,9 @@ int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
 
        /* Person/date information */
        if (!author)
-               author = git_author_info(IDENT_ERROR_ON_NO_NAME);
+               author = git_author_info(IDENT_STRICT);
        strbuf_addf(&buffer, "author %s\n", author);
-       strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
+       strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
        if (!encoding_is_utf8)
                strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
 
index eeee986022fc8f235c43a2d45eb942fc0095f8b1..71ef171cab78bfdb9d6421f2a2b84acbce0bfc00 100644 (file)
--- a/config.c
+++ b/config.c
@@ -762,28 +762,6 @@ static int git_default_core_config(const char *var, const char *value)
        return 0;
 }
 
-static int git_default_user_config(const char *var, const char *value)
-{
-       if (!strcmp(var, "user.name")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               strlcpy(git_default_name, value, sizeof(git_default_name));
-               user_ident_explicitly_given |= IDENT_NAME_GIVEN;
-               return 0;
-       }
-
-       if (!strcmp(var, "user.email")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               strlcpy(git_default_email, value, sizeof(git_default_email));
-               user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
-               return 0;
-       }
-
-       /* Add other config variables here and to Documentation/config.txt. */
-       return 0;
-}
-
 static int git_default_i18n_config(const char *var, const char *value)
 {
        if (!strcmp(var, "i18n.commitencoding"))
@@ -870,7 +848,7 @@ int git_default_config(const char *var, const char *value, void *dummy)
                return git_default_core_config(var, value);
 
        if (!prefixcmp(var, "user."))
-               return git_default_user_config(var, value);
+               return git_ident_config(var, value, dummy);
 
        if (!prefixcmp(var, "i18n."))
                return git_default_i18n_config(var, value);
index d7e6c657631f05553250a1705ea6c77c375c4bf4..669e498f5a245909725494feda2531f0088f8160 100644 (file)
@@ -11,9 +11,6 @@
 #include "refs.h"
 #include "fmt-merge-msg.h"
 
-char git_default_email[MAX_GITNAME];
-char git_default_name[MAX_GITNAME];
-int user_ident_explicitly_given;
 int trust_executable_bit = 1;
 int trust_ctime = 1;
 int has_symlinks = 1;
index ed11ad8119bb22db8a5428aed5525c40fbb72217..5bd9ad7d2a23773b1410ded9f4f241ebe4d4da00 100644 (file)
@@ -595,4 +595,7 @@ int rmdir_or_warn(const char *path);
  */
 int remove_or_warn(unsigned int mode, const char *path);
 
+/* Get the passwd entry for the UID of the current process. */
+struct passwd *xgetpwuid_self(void);
+
 #endif
index 09ab64aa24a1239cdb1ed5259c0b975a89594e9c..0863c618007a02fa20c27b62614de8a554128d0c 100644 (file)
@@ -30,7 +30,7 @@ const char *get_signing_key(void)
 {
        if (configured_signing_key)
                return configured_signing_key;
-       return git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE);
+       return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
 }
 
 /*
index 1df7ab5670d0ba2823da14083934925b0f05c926..a832ca77a31245e8ef9c71adcc0d7110fcda1e5c 100644 (file)
@@ -904,7 +904,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
                ep = strchr(ep + 1, '/');
        }
 
-       escaped = xml_entities(git_default_email);
+       escaped = xml_entities(ident_default_email());
        strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
        free(escaped);
 
diff --git a/ident.c b/ident.c
index 5df094d175ab91afb9ead68431f0c99bde7fd03f..443c0751bd1f63c059649f9f7f9174349cc1d270 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -7,7 +7,10 @@
  */
 #include "cache.h"
 
+static struct strbuf git_default_name = STRBUF_INIT;
+static struct strbuf git_default_email = STRBUF_INIT;
 static char git_default_date[50];
+int user_ident_explicitly_given;
 
 #ifdef NO_GECOS_IN_PWENT
 #define get_gecos(ignored) "&"
@@ -15,42 +18,27 @@ static char git_default_date[50];
 #define get_gecos(struct_passwd) ((struct_passwd)->pw_gecos)
 #endif
 
-static void copy_gecos(const struct passwd *w, char *name, size_t sz)
+static void copy_gecos(const struct passwd *w, struct strbuf *name)
 {
-       char *src, *dst;
-       size_t len, nlen;
-
-       nlen = strlen(w->pw_name);
+       char *src;
 
        /* Traditionally GECOS field had office phone numbers etc, separated
         * with commas.  Also & stands for capitalized form of the login name.
         */
 
-       for (len = 0, dst = name, src = get_gecos(w); len < sz; src++) {
+       for (src = get_gecos(w); *src && *src != ','; src++) {
                int ch = *src;
-               if (ch != '&') {
-                       *dst++ = ch;
-                       if (ch == 0 || ch == ',')
-                               break;
-                       len++;
-                       continue;
-               }
-               if (len + nlen < sz) {
+               if (ch != '&')
+                       strbuf_addch(name, ch);
+               else {
                        /* Sorry, Mr. McDonald... */
-                       *dst++ = toupper(*w->pw_name);
-                       memcpy(dst, w->pw_name + 1, nlen - 1);
-                       dst += nlen - 1;
-                       len += nlen;
+                       strbuf_addch(name, toupper(*w->pw_name));
+                       strbuf_addstr(name, w->pw_name + 1);
                }
        }
-       if (len < sz)
-               name[len] = 0;
-       else
-               die("Your parents must have hated you!");
-
 }
 
-static int add_mailname_host(char *buf, size_t len)
+static int add_mailname_host(struct strbuf *buf)
 {
        FILE *mailname;
 
@@ -61,7 +49,7 @@ static int add_mailname_host(char *buf, size_t len)
                                strerror(errno));
                return -1;
        }
-       if (!fgets(buf, len, mailname)) {
+       if (strbuf_getline(buf, mailname, '\n') == EOF) {
                if (ferror(mailname))
                        warning("cannot read /etc/mailname: %s",
                                strerror(errno));
@@ -73,94 +61,67 @@ static int add_mailname_host(char *buf, size_t len)
        return 0;
 }
 
-static void add_domainname(char *buf, size_t len)
+static void add_domainname(struct strbuf *out)
 {
+       char buf[1024];
        struct hostent *he;
-       size_t namelen;
-       const char *domainname;
 
-       if (gethostname(buf, len)) {
+       if (gethostname(buf, sizeof(buf))) {
                warning("cannot get host name: %s", strerror(errno));
-               strlcpy(buf, "(none)", len);
+               strbuf_addstr(out, "(none)");
                return;
        }
-       namelen = strlen(buf);
-       if (memchr(buf, '.', namelen))
-               return;
-
-       he = gethostbyname(buf);
-       buf[namelen++] = '.';
-       buf += namelen;
-       len -= namelen;
-       if (he && (domainname = strchr(he->h_name, '.')))
-               strlcpy(buf, domainname + 1, len);
+       if (strchr(buf, '.'))
+               strbuf_addstr(out, buf);
+       else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
+               strbuf_addstr(out, he->h_name);
        else
-               strlcpy(buf, "(none)", len);
+               strbuf_addf(out, "%s.(none)", buf);
 }
 
-static void copy_email(const struct passwd *pw)
+static void copy_email(const struct passwd *pw, struct strbuf *email)
 {
        /*
         * Make up a fake email address
         * (name + '@' + hostname [+ '.' + domainname])
         */
-       size_t len = strlen(pw->pw_name);
-       if (len > sizeof(git_default_email)/2)
-               die("Your sysadmin must hate you!");
-       memcpy(git_default_email, pw->pw_name, len);
-       git_default_email[len++] = '@';
-
-       if (!add_mailname_host(git_default_email + len,
-                               sizeof(git_default_email) - len))
+       strbuf_addstr(email, pw->pw_name);
+       strbuf_addch(email, '@');
+
+       if (!add_mailname_host(email))
                return; /* read from "/etc/mailname" (Debian) */
-       add_domainname(git_default_email + len,
-                       sizeof(git_default_email) - len);
+       add_domainname(email);
 }
 
-static void setup_ident(const char **name, const char **emailp)
+const char *ident_default_name(void)
 {
-       struct passwd *pw = NULL;
-
-       /* Get the name ("gecos") */
-       if (!*name && !git_default_name[0]) {
-               pw = getpwuid(getuid());
-               if (!pw)
-                       die("You don't exist. Go away!");
-               copy_gecos(pw, git_default_name, sizeof(git_default_name));
+       if (!git_default_name.len) {
+               copy_gecos(xgetpwuid_self(), &git_default_name);
+               strbuf_trim(&git_default_name);
        }
-       if (!*name)
-               *name = git_default_name;
+       return git_default_name.buf;
+}
 
-       if (!*emailp && !git_default_email[0]) {
+const char *ident_default_email(void)
+{
+       if (!git_default_email.len) {
                const char *email = getenv("EMAIL");
 
                if (email && email[0]) {
-                       strlcpy(git_default_email, email,
-                               sizeof(git_default_email));
+                       strbuf_addstr(&git_default_email, email);
                        user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
-               } else {
-                       if (!pw)
-                               pw = getpwuid(getuid());
-                       if (!pw)
-                               die("You don't exist. Go away!");
-                       copy_email(pw);
-               }
+               } else
+                       copy_email(xgetpwuid_self(), &git_default_email);
+               strbuf_trim(&git_default_email);
        }
-       if (!*emailp)
-               *emailp = git_default_email;
-
-       /* And set the default date */
-       if (!git_default_date[0])
-               datestamp(git_default_date, sizeof(git_default_date));
+       return git_default_email.buf;
 }
 
-static int add_raw(char *buf, size_t size, int offset, const char *str)
+const char *ident_default_date(void)
 {
-       size_t len = strlen(str);
-       if (offset + len > size)
-               return size;
-       memcpy(buf + offset, str, len);
-       return offset + len;
+       if (!git_default_date[0])
+               datestamp(git_default_date, sizeof(git_default_date));
+       return git_default_date;
 }
 
 static int crud(unsigned char c)
@@ -181,7 +142,7 @@ static int crud(unsigned char c)
  * Copy over a string to the destination, but avoid special
  * characters ('\n', '<' and '>') and remove crud at the end
  */
-static int copy(char *buf, size_t size, int offset, const char *src)
+static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src)
 {
        size_t i, len;
        unsigned char c;
@@ -205,19 +166,19 @@ static int copy(char *buf, size_t size, int offset, const char *src)
        /*
         * Copy the rest to the buffer, but avoid the special
         * characters '\n' '<' and '>' that act as delimiters on
-        * an identification line
+        * an identification line. We can only remove crud, never add it,
+        * so 'len' is our maximum.
         */
+       strbuf_grow(sb, len);
        for (i = 0; i < len; i++) {
                c = *src++;
                switch (c) {
                case '\n': case '<': case '>':
                        continue;
                }
-               if (offset >= size)
-                       return size;
-               buf[offset++] = c;
+               sb->buf[sb->len++] = c;
        }
-       return offset;
+       sb->buf[sb->len] = '\0';
 }
 
 /*
@@ -304,57 +265,62 @@ static const char *env_hint =
 const char *fmt_ident(const char *name, const char *email,
                      const char *date_str, int flag)
 {
-       static char buffer[1000];
+       static struct strbuf ident = STRBUF_INIT;
        char date[50];
-       int i;
-       int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
-       int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
-       int name_addr_only = (flag & IDENT_NO_DATE);
+       int strict = (flag & IDENT_STRICT);
+       int want_date = !(flag & IDENT_NO_DATE);
+       int want_name = !(flag & IDENT_NO_NAME);
 
-       setup_ident(&name, &email);
+       if (want_name && !name)
+               name = ident_default_name();
+       if (!email)
+               email = ident_default_email();
 
-       if (!*name) {
+       if (want_name && !*name) {
                struct passwd *pw;
 
-               if ((warn_on_no_name || error_on_no_name) &&
-                   name == git_default_name && env_hint) {
-                       fputs(env_hint, stderr);
-                       env_hint = NULL; /* warn only once */
+               if (strict) {
+                       if (name == git_default_name.buf)
+                               fputs(env_hint, stderr);
+                       die("empty ident name (for <%s>) not allowed", email);
                }
-               if (error_on_no_name)
-                       die("empty ident %s <%s> not allowed", name, email);
-               pw = getpwuid(getuid());
-               if (!pw)
-                       die("You don't exist. Go away!");
-               strlcpy(git_default_name, pw->pw_name,
-                       sizeof(git_default_name));
-               name = git_default_name;
+               pw = xgetpwuid_self();
+               name = pw->pw_name;
+       }
+
+       if (strict && email == git_default_email.buf &&
+           strstr(email, "(none)")) {
+               fputs(env_hint, stderr);
+               die("unable to auto-detect email address (got '%s')", email);
        }
 
-       strcpy(date, git_default_date);
-       if (!name_addr_only && date_str && date_str[0]) {
-               if (parse_date(date_str, date, sizeof(date)) < 0)
-                       die("invalid date format: %s", date_str);
+       if (want_date) {
+               if (date_str && date_str[0]) {
+                       if (parse_date(date_str, date, sizeof(date)) < 0)
+                               die("invalid date format: %s", date_str);
+               }
+               else
+                       strcpy(date, ident_default_date());
        }
 
-       i = copy(buffer, sizeof(buffer), 0, name);
-       i = add_raw(buffer, sizeof(buffer), i, " <");
-       i = copy(buffer, sizeof(buffer), i, email);
-       if (!name_addr_only) {
-               i = add_raw(buffer, sizeof(buffer), i,  "> ");
-               i = copy(buffer, sizeof(buffer), i, date);
-       } else {
-               i = add_raw(buffer, sizeof(buffer), i, ">");
+       strbuf_reset(&ident);
+       if (want_name) {
+               strbuf_addstr_without_crud(&ident, name);
+               strbuf_addstr(&ident, " <");
        }
-       if (i >= sizeof(buffer))
-               die("Impossibly long personal identifier");
-       buffer[i] = 0;
-       return buffer;
+       strbuf_addstr_without_crud(&ident, email);
+       if (want_name)
+                       strbuf_addch(&ident, '>');
+       if (want_date) {
+               strbuf_addch(&ident, ' ');
+               strbuf_addstr_without_crud(&ident, date);
+       }
+       return ident.buf;
 }
 
 const char *fmt_name(const char *name, const char *email)
 {
-       return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
+       return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE);
 }
 
 const char *git_author_info(int flag)
@@ -385,3 +351,26 @@ int user_ident_sufficiently_given(void)
        return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
 #endif
 }
+
+int git_ident_config(const char *var, const char *value, void *data)
+{
+       if (!strcmp(var, "user.name")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               strbuf_reset(&git_default_name);
+               strbuf_addstr(&git_default_name, value);
+               user_ident_explicitly_given |= IDENT_NAME_GIVEN;
+               return 0;
+       }
+
+       if (!strcmp(var, "user.email")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               strbuf_reset(&git_default_email);
+               strbuf_addstr(&git_default_email, value);
+               user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
+               return 0;
+       }
+
+       return 0;
+}
index 376d97317674dfe6dcffec3772c899c4fc96f82f..c894930c1863bfcf33a01b2d98c48478aa874186 100644 (file)
@@ -299,19 +299,22 @@ static unsigned int digits_in_number(unsigned int number)
        return result;
 }
 
-void get_patch_filename(struct commit *commit, int nr, const char *suffix,
-                       struct strbuf *buf)
+void get_patch_filename(struct commit *commit, const char *subject, int nr,
+                       const char *suffix, struct strbuf *buf)
 {
        int suffix_len = strlen(suffix) + 1;
        int start_len = buf->len;
 
-       strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
-       if (commit) {
+       strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr);
+       if (commit || subject) {
                int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
                struct pretty_print_context ctx = {0};
-               ctx.date_mode = DATE_NORMAL;
 
-               format_commit_message(commit, "%f", buf, &ctx);
+               if (subject)
+                       strbuf_addstr(buf, subject);
+               else if (commit)
+                       format_commit_message(commit, "%f", buf, &ctx);
+
                if (max_len < buf->len)
                        strbuf_setlen(buf, max_len);
                strbuf_addstr(buf, suffix);
@@ -384,8 +387,8 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                         mime_boundary_leader, opt->mime_boundary);
                extra_headers = subject_buffer;
 
-               get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
-                                   opt->patch_suffix, &filename);
+               get_patch_filename(opt->numbered_files ? NULL : commit, NULL,
+                                  opt->nr, opt->patch_suffix, &filename);
                snprintf(buffer, sizeof(buffer) - 1,
                         "\n--%s%s\n"
                         "Content-Type: text/x-patch;"
index 5c4cf7cac3327e2ef6000dff414680cd5c4c0e1e..f5ac238bba478851bb10b09f32ea75da05475267 100644 (file)
@@ -21,7 +21,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 void load_ref_decorations(int flags);
 
 #define FORMAT_PATCH_NAME_MAX 64
-void get_patch_filename(struct commit *commit, int nr, const char *suffix,
-                       struct strbuf *buf);
+void get_patch_filename(struct commit *commit, const char *subject, int nr,
+                       const char *suffix, struct strbuf *buf);
 
 #endif
diff --git a/refs.c b/refs.c
index 010ed07e33d90cbdae4511013319a129c1201ca9..da74a2b29a20a2c94fef61641e35980007c1ab95 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -259,8 +259,13 @@ static void clear_ref_dir(struct ref_dir *dir);
 
 static void free_ref_entry(struct ref_entry *entry)
 {
-       if (entry->flag & REF_DIR)
-               clear_ref_dir(get_ref_dir(entry));
+       if (entry->flag & REF_DIR) {
+               /*
+                * Do not use get_ref_dir() here, as that might
+                * trigger the reading of loose refs.
+                */
+               clear_ref_dir(&entry->u.subdir);
+       }
        free(entry);
 }
 
@@ -300,12 +305,13 @@ static void clear_ref_dir(struct ref_dir *dir)
  * "refs/heads/") or "" for the top-level directory.
  */
 static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
-                                         const char *dirname, int incomplete)
+                                         const char *dirname, size_t len,
+                                         int incomplete)
 {
        struct ref_entry *direntry;
-       int len = strlen(dirname);
        direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
-       memcpy(direntry->name, dirname, len + 1);
+       memcpy(direntry->name, dirname, len);
+       direntry->name[len] = '\0';
        direntry->u.subdir.ref_cache = ref_cache;
        direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
        return direntry;
@@ -320,28 +326,42 @@ static int ref_entry_cmp(const void *a, const void *b)
 
 static void sort_ref_dir(struct ref_dir *dir);
 
+struct string_slice {
+       size_t len;
+       const char *str;
+};
+
+static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
+{
+       struct string_slice *key = (struct string_slice *)key_;
+       struct ref_entry *ent = *(struct ref_entry **)ent_;
+       int entlen = strlen(ent->name);
+       int cmplen = key->len < entlen ? key->len : entlen;
+       int cmp = memcmp(key->str, ent->name, cmplen);
+       if (cmp)
+               return cmp;
+       return key->len - entlen;
+}
+
 /*
  * Return the entry with the given refname from the ref_dir
  * (non-recursively), sorting dir if necessary.  Return NULL if no
  * such entry is found.  dir must already be complete.
  */
-static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)
+static struct ref_entry *search_ref_dir(struct ref_dir *dir,
+                                       const char *refname, size_t len)
 {
-       struct ref_entry *e, **r;
-       int len;
+       struct ref_entry **r;
+       struct string_slice key;
 
        if (refname == NULL || !dir->nr)
                return NULL;
 
        sort_ref_dir(dir);
-
-       len = strlen(refname) + 1;
-       e = xmalloc(sizeof(struct ref_entry) + len);
-       memcpy(e->name, refname, len);
-
-       r = bsearch(&e, dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
-
-       free(e);
+       key.len = len;
+       key.str = refname;
+       r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
+                   ref_entry_cmp_sslice);
 
        if (r == NULL)
                return NULL;
@@ -357,9 +377,10 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname
  * directory cannot be found.  dir must already be complete.
  */
 static struct ref_dir *search_for_subdir(struct ref_dir *dir,
-                                        const char *subdirname, int mkdir)
+                                        const char *subdirname, size_t len,
+                                        int mkdir)
 {
-       struct ref_entry *entry = search_ref_dir(dir, subdirname);
+       struct ref_entry *entry = search_ref_dir(dir, subdirname, len);
        if (!entry) {
                if (!mkdir)
                        return NULL;
@@ -369,7 +390,7 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
                 * therefore, create an empty record for it but mark
                 * the record complete.
                 */
-               entry = create_dir_entry(dir->ref_cache, subdirname, 0);
+               entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
                add_entry_to_dir(dir, entry);
        }
        return get_ref_dir(entry);
@@ -387,15 +408,11 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
 static struct ref_dir *find_containing_dir(struct ref_dir *dir,
                                           const char *refname, int mkdir)
 {
-       struct strbuf dirname;
        const char *slash;
-       strbuf_init(&dirname, PATH_MAX);
        for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+               size_t dirnamelen = slash - refname + 1;
                struct ref_dir *subdir;
-               strbuf_add(&dirname,
-                          refname + dirname.len,
-                          (slash + 1) - (refname + dirname.len));
-               subdir = search_for_subdir(dir, dirname.buf, mkdir);
+               subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
                if (!subdir) {
                        dir = NULL;
                        break;
@@ -403,7 +420,6 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,
                dir = subdir;
        }
 
-       strbuf_release(&dirname);
        return dir;
 }
 
@@ -418,7 +434,7 @@ static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
        dir = find_containing_dir(dir, refname, 0);
        if (!dir)
                return NULL;
-       entry = search_ref_dir(dir, refname);
+       entry = search_ref_dir(dir, refname, strlen(refname));
        return (entry && !(entry->flag & REF_DIR)) ? entry : NULL;
 }
 
@@ -828,7 +844,7 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)
                const char *packed_refs_file;
                FILE *f;
 
-               refs->packed = create_dir_entry(refs, "", 0);
+               refs->packed = create_dir_entry(refs, "", 0, 0);
                if (*refs->name)
                        packed_refs_file = git_path_submodule(refs->name, "packed-refs");
                else
@@ -893,7 +909,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
                        add_entry_to_dir(dir,
-                                        create_dir_entry(refs, refname.buf, 1));
+                                        create_dir_entry(refs, refname.buf,
+                                                         refname.len, 1));
                } else {
                        if (*refs->name) {
                                hashclr(sha1);
@@ -923,12 +940,12 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs)
                 * are about to read the only subdirectory that can
                 * hold references:
                 */
-               refs->loose = create_dir_entry(refs, "", 0);
+               refs->loose = create_dir_entry(refs, "", 0, 0);
                /*
                 * Create an incomplete entry for "refs/":
                 */
                add_entry_to_dir(get_ref_dir(refs->loose),
-                                create_dir_entry(refs, "refs/", 1));
+                                create_dir_entry(refs, "refs/", 5, 1));
        }
        return get_ref_dir(refs->loose);
 }
index b296d174043b5e5a11aceb9940ba6ba035e77678..6833538829e1d3e5fe4a525a1b44cbebd9f96efb 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -7,6 +7,7 @@
 #include "dir.h"
 #include "tag.h"
 #include "string-list.h"
+#include "mergesort.h"
 
 enum map_direction { FROM_SRC, FROM_DST };
 
@@ -918,6 +919,27 @@ void free_refs(struct ref *ref)
        }
 }
 
+int ref_compare_name(const void *va, const void *vb)
+{
+       const struct ref *a = va, *b = vb;
+       return strcmp(a->name, b->name);
+}
+
+static void *ref_list_get_next(const void *a)
+{
+       return ((const struct ref *)a)->next;
+}
+
+static void ref_list_set_next(void *a, void *next)
+{
+       ((struct ref *)a)->next = next;
+}
+
+void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
+{
+       *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
+}
+
 static int count_refspec_match(const char *pattern,
                               struct ref *refs,
                               struct ref **matched_ref)
index 9ad8eb6cc68b0842d8dc2aef9a65c25524739d97..251d8fd9654f23e7a131765742803492fb0d9041 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -72,6 +72,8 @@ extern const struct refspec *tag_refspec;
 struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
+void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
+int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
 
index 3a3cd1206af5465323760e952a836935ac271d32..4d978e54e4fb9d84e81977539ffa083534ef2968 100644 (file)
@@ -121,7 +121,7 @@ static enum input_source istream_source(const unsigned char *sha1,
        case OI_LOOSE:
                return loose;
        case OI_PACKED:
-               if (!oi->u.packed.is_delta && big_file_threshold <= size)
+               if (!oi->u.packed.is_delta && big_file_threshold < size)
                        return pack_non_delta;
                /* fallthru */
        default:
index 6972258b27f6039e05f6bd2129f9c18ca45404d4..c6feca44e3677c416bfe4841fda5205b2133f7bc 100755 (executable)
@@ -3,7 +3,10 @@
 test_description='test local clone'
 . ./test-lib.sh
 
-D=`pwd`
+repo_is_hardlinked() {
+       find "$1/objects" -type f -links 1 >output &&
+       test_line_count = 0 output
+}
 
 test_expect_success 'preparing origin repository' '
        : >file && git add . && git commit -m1 &&
@@ -19,105 +22,72 @@ test_expect_success 'preparing origin repository' '
 '
 
 test_expect_success 'local clone without .git suffix' '
-       cd "$D" &&
        git clone -l -s a b &&
-       cd b &&
+       (cd b &&
        test "$(GIT_CONFIG=.git/config git config --bool core.bare)" = false &&
-       git fetch
+       git fetch)
 '
 
 test_expect_success 'local clone with .git suffix' '
-       cd "$D" &&
        git clone -l -s a.git c &&
-       cd c &&
-       git fetch
+       (cd c && git fetch)
 '
 
 test_expect_success 'local clone from x' '
-       cd "$D" &&
        git clone -l -s x y &&
-       cd y &&
-       git fetch
+       (cd y && git fetch)
 '
 
 test_expect_success 'local clone from x.git that does not exist' '
-       cd "$D" &&
-       if git clone -l -s x.git z
-       then
-               echo "Oops, should have failed"
-               false
-       else
-               echo happy
-       fi
+       test_must_fail git clone -l -s x.git z
 '
 
 test_expect_success 'With -no-hardlinks, local will make a copy' '
-       cd "$D" &&
        git clone --bare --no-hardlinks x w &&
-       cd w &&
-       linked=$(find objects -type f ! -links 1 | wc -l) &&
-       test 0 = $linked
+       ! repo_is_hardlinked w
 '
 
 test_expect_success 'Even without -l, local will make a hardlink' '
-       cd "$D" &&
        rm -fr w &&
        git clone -l --bare x w &&
-       cd w &&
-       copied=$(find objects -type f -links 1 | wc -l) &&
-       test 0 = $copied
+       repo_is_hardlinked w
 '
 
 test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
-       cd "$D" &&
        echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
        git clone a d &&
-       cd d &&
+       (cd d &&
        git fetch &&
-       test ! -e .git/refs/remotes/origin/HEAD'
+       test ! -e .git/refs/remotes/origin/HEAD)
+'
 
 test_expect_success 'bundle clone without .bundle suffix' '
-       cd "$D" &&
        git clone dir/b3 &&
-       cd b3 &&
-       git fetch
+       (cd b3 && git fetch)
 '
 
 test_expect_success 'bundle clone with .bundle suffix' '
-       cd "$D" &&
        git clone b1.bundle &&
-       cd b1 &&
-       git fetch
+       (cd b1 && git fetch)
 '
 
 test_expect_success 'bundle clone from b4' '
-       cd "$D" &&
        git clone b4 bdl &&
-       cd bdl &&
-       git fetch
+       (cd bdl && git fetch)
 '
 
 test_expect_success 'bundle clone from b4.bundle that does not exist' '
-       cd "$D" &&
-       if git clone b4.bundle bb
-       then
-               echo "Oops, should have failed"
-               false
-       else
-               echo happy
-       fi
+       test_must_fail git clone b4.bundle bb
 '
 
 test_expect_success 'bundle clone with nonexistent HEAD' '
-       cd "$D" &&
        git clone b2.bundle b2 &&
-       cd b2 &&
+       (cd b2 &&
        git fetch &&
-       test ! -e .git/refs/heads/master
+       test_must_fail git rev-parse --verify refs/heads/master)
 '
 
 test_expect_success 'clone empty repository' '
-       cd "$D" &&
        mkdir empty &&
        (cd empty &&
         git init &&
@@ -135,7 +105,6 @@ test_expect_success 'clone empty repository' '
 '
 
 test_expect_success 'clone empty repository, and then push should not segfault.' '
-       cd "$D" &&
        rm -fr empty/ empty-clone/ &&
        mkdir empty &&
        (cd empty && git init) &&
@@ -145,13 +114,11 @@ test_expect_success 'clone empty repository, and then push should not segfault.'
 '
 
 test_expect_success 'cloning non-existent directory fails' '
-       cd "$D" &&
        rm -rf does-not-exist &&
        test_must_fail git clone does-not-exist
 '
 
 test_expect_success 'cloning non-git directory fails' '
-       cd "$D" &&
        rm -rf not-a-git-repo not-a-git-repo-clone &&
        mkdir not-a-git-repo &&
        test_must_fail git clone not-a-git-repo not-a-git-repo-clone
index 2859256de30deec3bdb7ceeef51b12342a901ed0..99fe16b72d448e9b5cf686304cb040c221ad1f44 100755 (executable)
@@ -218,7 +218,7 @@ test_expect_success 'git p4 clone simple branches' '
                cd branch1 &&
                p4 edit file2 &&
                echo file2_ >>file2 &&
-               p4 submit -d "update file2 in branch3" &&
+               p4 submit -d "update file2 in branch1" &&
                cd "$git" &&
                git reset --hard p4/depot/branch1 &&
                git p4 rebase &&
@@ -249,8 +249,6 @@ test_expect_success 'git p4 clone simple branches' '
 #   `- file2
 #   `- file3
 test_expect_success 'git p4 add complex branches' '
-       test_when_finished cleanup_git &&
-       test_create_repo "$git" &&
        (
                cd "$cli" &&
                changelist=$(p4 changes -m1 //depot/... | cut -d" " -f2) &&
@@ -306,6 +304,112 @@ test_expect_success 'git p4 clone complex branches' '
        )
 '
 
+# Move branch3/file3 to branch4/file3 in a single changelist
+test_expect_success 'git p4 submit to two branches in a single changelist' '
+       (
+               cd "$cli" &&
+               p4 integrate //depot/branch3/file3 //depot/branch4/file3 &&
+               p4 delete //depot/branch3/file3 &&
+               p4 submit -d "Move branch3/file3 to branch4/file3"
+       )
+'
+
+# Confirm that changes to two branches done in a single changelist
+# are correctly imported by git p4
+test_expect_success 'git p4 sync changes to two branches in the same changelist' '
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git config git-p4.branchList branch1:branch2 &&
+               git config --add git-p4.branchList branch1:branch3 &&
+               git config --add git-p4.branchList branch1:branch4 &&
+               git config --add git-p4.branchList branch1:branch5 &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
+               git log --all --graph --decorate --stat &&
+               git reset --hard p4/depot/branch1 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_file file3 &&
+               grep update file2 &&
+               git reset --hard p4/depot/branch2 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_missing file3 &&
+               ! grep update file2 &&
+               git reset --hard p4/depot/branch3 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_missing file3 &&
+               grep update file2 &&
+               git reset --hard p4/depot/branch4 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_file file3 &&
+               ! grep update file2 &&
+               git reset --hard p4/depot/branch5 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_file file3 &&
+               ! grep update file2 &&
+               test_path_is_missing .git/git-p4-tmp
+       )
+'
+
+# Create a branch by integrating a single file
+test_expect_success 'git p4 file subset branch' '
+       (
+               cd "$cli" &&
+               p4 integrate //depot/branch1/file1 //depot/branch6/file1 &&
+               p4 submit -d "Integrate file1 alone from branch1 to branch6"
+       )
+'
+
+# Check if git p4 creates a new branch containing a single file,
+# instead of keeping the old files from the original branch
+test_expect_failure 'git p4 clone file subset branch' '
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git config git-p4.branchList branch1:branch2 &&
+               git config --add git-p4.branchList branch1:branch3 &&
+               git config --add git-p4.branchList branch1:branch4 &&
+               git config --add git-p4.branchList branch1:branch5 &&
+               git config --add git-p4.branchList branch1:branch6 &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
+               git log --all --graph --decorate --stat &&
+               git reset --hard p4/depot/branch1 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_file file3 &&
+               grep update file2 &&
+               git reset --hard p4/depot/branch2 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_missing file3 &&
+               ! grep update file2 &&
+               git reset --hard p4/depot/branch3 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_missing file3 &&
+               grep update file2 &&
+               git reset --hard p4/depot/branch4 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_file file3 &&
+               ! grep update file2 &&
+               git reset --hard p4/depot/branch5 &&
+               test_path_is_file file1 &&
+               test_path_is_file file2 &&
+               test_path_is_file file3 &&
+               ! grep update file2 &&
+               git reset --hard p4/depot/branch6 &&
+               test_path_is_file file1 &&
+               test_path_is_missing file2 &&
+               test_path_is_missing file3
+       )
+'
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 6ccd0595f43d0ef62bd60a5863804f9a842a4235..b5e33e49c77bdf1d19292971b63ff5221b013f33 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -402,3 +402,15 @@ int remove_or_warn(unsigned int mode, const char *file)
 {
        return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
 }
+
+struct passwd *xgetpwuid_self(void)
+{
+       struct passwd *pw;
+
+       errno = 0;
+       pw = getpwuid(getuid());
+       if (!pw)
+               die(_("unable to look up current user in the passwd file: %s"),
+                   errno ? strerror(errno) : _("no such user"));
+       return pw;
+}