]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'rs/gc-sort-func-cast-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 25 Nov 2020 23:24:53 +0000 (15:24 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Nov 2020 23:24:53 +0000 (15:24 -0800)
Fix broken sorting of maintenance tasks.

* rs/gc-sort-func-cast-fix:
  gc: fix cast in compare_tasks_by_selection()

72 files changed:
Documentation/RelNotes/2.30.0.txt
Documentation/config/format.txt
Documentation/git-format-patch.txt
Documentation/git-rev-parse.txt
Makefile
add-interactive.c
archive.c
archive.h
attr.c
bisect.c
blame.c
block-sha1/sha1.c
block-sha1/sha1.h
bloom.c
builtin/difftool.c
builtin/fetch.c
builtin/index-pack.c
builtin/log.c
builtin/pack-redundant.c
builtin/receive-pack.c
builtin/rev-parse.c
builtin/shortlog.c
ci/print-test-failures.sh
config.c
config.mak.uname
contrib/completion/git-completion.bash
csum-file.h
diff.c
diffcore-rename.c
dir.c
git-mergetool--lib.sh
hashmap.c
hashmap.h
list-objects-filter-options.c
log-tree.c
log-tree.h
merge-recursive.c
mergetools/bc
midx.c
name-hash.c
object.c
oidmap.c
pack-check.c
pack-revindex.c
pack-write.c
packfile.c
patch-ids.c
range-diff.c
ref-filter.c
revision.c
revision.h
sequencer.c
strmap.c [new file with mode: 0644]
strmap.h [new file with mode: 0644]
submodule-config.c
submodule.c
t/helper/test-hashmap.c
t/helper/test-proc-receive.c
t/t1503-rev-parse-verify.sh
t/t1506-rev-parse-diagnosis.sh
t/t4014-format-patch.sh
t/t5310-pack-bitmaps.sh
t/t5411/common-functions.sh
t/t5411/test-0000-standard-git-push.sh
t/t5411/test-0001-standard-git-push--porcelain.sh
t/t5411/test-0013-bad-protocol.sh
t/t5411/test-0014-bad-protocol--porcelain.sh
t/t5411/test-0026-push-options.sh
t/t5411/test-0027-push-options--porcelain.sh
t/t5526-fetch-submodules.sh
t/t7800-difftool.sh
t/t9902-completion.sh

index 0933a7f84fb5fde3d375ee926833a7b28d2e070a..4c5be2dd046318b06c81fbc780158cc86510c306 100644 (file)
@@ -68,6 +68,13 @@ UI, Workflows & Features
 
  * Zsh autocompletion (in contrib/) update.
 
+ * The maximum length of output filenames "git format-patch" creates
+   has become configurable (used to be capped at 64).
+
+ * "git rev-parse" learned the "--end-of-options" to help scripts to
+   safely take a parameter that is supposed to be a revision, e.g.
+   "git rev-parse --verify -q --end-of-options $rev".
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -107,6 +114,9 @@ Performance, Internal Implementation, Development Support etc.
  * "git fetch --depth=<n>" over the stateless RPC / smart HTTP
    transport handled EOF from the client poorly at the server end.
 
+ * A specialization of hashmap that uses a string as key has been
+   introduced.  Hopefully it will see wider use over time.
+
 
 Fixes since v2.29
 -----------------
@@ -217,6 +227,16 @@ Fixes since v2.29
    non-existent object name in the input, instead of complaining.
    (merge c714d05875 jc/blame-ignore-fix later to maint).
 
+ * Running "git diff" while allowing external diff in a state with
+   unmerged paths used to segfault, which has been corrected.
+   (merge d66851806f jk/diff-release-filespec-fix later to maint).
+
+ * Build configuration cleanup.
+   (merge b990f02fd8 ab/config-mak-uname-simplify later to maint).
+
+ * Fix regression introduced when nvimdiff support in mergetool was added.
+   (merge 12026f46e7 pd/mergetool-nvimdiff later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 3e0a5dc9af cc/doc-filter-branch-typofix later to maint).
    (merge 32c83afc2c cw/ci-ghwf-check-ws-errors later to maint).
index 851bf410a3bba8ff41e78993b9642d7a23d1652a..fdbc06a4d2a837f768a213fd5ffdf28ad5a48315 100644 (file)
@@ -94,6 +94,11 @@ format.outputDirectory::
        Set a custom directory to store the resulting files instead of the
        current working directory. All directory components will be created.
 
+format.filenameMaxLength::
+       The maximum length of the output filenames generated by the
+       `format-patch` command; defaults to 64.  Can be overridden
+       by the `--filename-max-length=<n>` command line option.
+
 format.useAutoBase::
        A boolean value which lets you enable the `--base=auto` option of
        format-patch by default. Can also be set to "whenAble" to allow
index bf1bb40f6303fab2a8362af493b1839af21471f2..3e49bf221087c06c7850e66cdc833b92d7dcb9bc 100644 (file)
@@ -28,6 +28,7 @@ SYNOPSIS
                   [--no-notes | --notes[=<ref>]]
                   [--interdiff=<previous>]
                   [--range-diff=<previous> [--creation-factor=<percent>]]
+                  [--filename-max-length=<n>]
                   [--progress]
                   [<common diff options>]
                   [ <since> | <revision range> ]
@@ -200,6 +201,13 @@ populated with placeholder text.
        allows for useful naming of a patch series, and can be
        combined with the `--numbered` option.
 
+--filename-max-length=<n>::
+       Instead of the standard 64 bytes, chomp the generated output
+       filenames at around '<n>' bytes (too short a value will be
+       silently raised to a reasonable length).  Defaults to the
+       value of the `format.filenameMaxLength` configuration
+       variable, or 64 if unconfigured.
+
 --rfc::
        Alias for `--subject-prefix="RFC PATCH"`. RFC means "Request For
        Comments"; use this when sending an experimental patch for
index 19b12b6d43ce5bfeb2dba36e30640ddaf0aca664..5013daa6efebdd92ea93138a751b5a1c61877a56 100644 (file)
@@ -109,6 +109,10 @@ names an existing object that is a commit-ish (i.e. a commit, or an
 annotated tag that points at a commit).  To make sure that `$VAR`
 names an existing object of any type, `git rev-parse "$VAR^{object}"`
 can be used.
++
+Note that if you are verifying a name from an untrusted source, it is
+wise to use `--end-of-options` so that the name argument is not mistaken
+for another option.
 
 -q::
 --quiet::
@@ -446,7 +450,7 @@ $ git rev-parse --verify HEAD
 * Print the commit object name from the revision in the $REV shell variable:
 +
 ------------
-$ git rev-parse --verify $REV^{commit}
+$ git rev-parse --verify --end-of-options $REV^{commit}
 ------------
 +
 This will error out if $REV is empty or not a valid revision.
@@ -454,7 +458,7 @@ This will error out if $REV is empty or not a valid revision.
 * Similar to above:
 +
 ------------
-$ git rev-parse --default master --verify $REV
+$ git rev-parse --default master --verify --end-of-options $REV
 ------------
 +
 but if $REV is empty, the commit object name from master will be printed.
index dd1cf41e007a0036e18eef4b0acae505ec52f168..d3a531d3c6b8c301309395e69115ac1ed3f11aa8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1004,6 +1004,7 @@ LIB_OBJS += stable-qsort.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
+LIB_OBJS += strmap.o
 LIB_OBJS += strvec.o
 LIB_OBJS += sub-process.o
 LIB_OBJS += submodule-config.o
index 555c4abf324f553b6bb70f6640554d407c12677a..a14c0feaa2a99e827a8e5c59db9f4c7d6ff38540 100644 (file)
@@ -557,7 +557,7 @@ static int get_modified_files(struct repository *r,
                if (ps)
                        clear_pathspec(&rev.prune_data);
        }
-       hashmap_free_entries(&s.file_map, struct pathname_entry, ent);
+       hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
        if (unmerged_count)
                *unmerged_count = s.unmerged_count;
        if (binary_count)
index 7a888c5338bc6ea57621845deadec8549445d6c5..5919d9e505088420802c1fd20bcfcaee62072c06 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -658,6 +658,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
        rc = ar->write_archive(ar, &args);
 
        string_list_clear_func(&args.extra_files, extra_file_info_clear);
+       free(args.refname);
 
        return rc;
 }
index e3d04e8ab313d2a86f7c040492999bf43d0874e8..33551b7ee17be6adf9c16221ef1f605dbf8d477c 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -8,7 +8,7 @@ struct repository;
 
 struct archiver_args {
        struct repository *repo;
-       const char *refname;
+       char *refname;
        const char *prefix;
        const char *base;
        size_t baselen;
diff --git a/attr.c b/attr.c
index a826b2ef1fabc8d4078d760fe3421d2a9cbbf2b2..4ef85d668b54960d692b6bae127920c10045c4ca 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -52,13 +52,6 @@ static inline void hashmap_unlock(struct attr_hashmap *map)
        pthread_mutex_unlock(&map->mutex);
 }
 
-/*
- * The global dictionary of all interned attributes.  This
- * is a singleton object which is shared between threads.
- * Access to this dictionary must be surrounded with a mutex.
- */
-static struct attr_hashmap g_attr_hashmap;
-
 /* The container for objects stored in "struct attr_hashmap" */
 struct attr_hash_entry {
        struct hashmap_entry ent;
@@ -80,11 +73,14 @@ static int attr_hash_entry_cmp(const void *unused_cmp_data,
        return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
 }
 
-/* Initialize an 'attr_hashmap' object */
-static void attr_hashmap_init(struct attr_hashmap *map)
-{
-       hashmap_init(&map->map, attr_hash_entry_cmp, NULL, 0);
-}
+/*
+ * The global dictionary of all interned attributes.  This
+ * is a singleton object which is shared between threads.
+ * Access to this dictionary must be surrounded with a mutex.
+ */
+static struct attr_hashmap g_attr_hashmap = {
+       HASHMAP_INIT(attr_hash_entry_cmp, NULL)
+};
 
 /*
  * Retrieve the 'value' stored in a hashmap given the provided 'key'.
@@ -96,9 +92,6 @@ static void *attr_hashmap_get(struct attr_hashmap *map,
        struct attr_hash_entry k;
        struct attr_hash_entry *e;
 
-       if (!map->map.tablesize)
-               attr_hashmap_init(map);
-
        hashmap_entry_init(&k.ent, memhash(key, keylen));
        k.key = key;
        k.keylen = keylen;
@@ -114,9 +107,6 @@ static void attr_hashmap_add(struct attr_hashmap *map,
 {
        struct attr_hash_entry *e;
 
-       if (!map->map.tablesize)
-               attr_hashmap_init(map);
-
        e = xmalloc(sizeof(struct attr_hash_entry));
        hashmap_entry_init(&e->ent, memhash(key, keylen));
        e->key = key;
index 58bc9c73a463d2f005587a5a68ab9b2a32198dcc..d8c2c8f7a78bf99e5f6d202ee7996ab9952b417f 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -103,8 +103,10 @@ static int count_interesting_parents(struct commit *commit, unsigned bisect_flag
        return count;
 }
 
-static inline int halfway(struct commit_list *p, int nr)
+static inline int approx_halfway(struct commit_list *p, int nr)
 {
+       int diff;
+
        /*
         * Don't short-cut something we are not going to return!
         */
@@ -113,13 +115,22 @@ static inline int halfway(struct commit_list *p, int nr)
        if (DEBUG_BISECT)
                return 0;
        /*
-        * 2 and 3 are halfway of 5.
+        * For small number of commits 2 and 3 are halfway of 5, and
         * 3 is halfway of 6 but 2 and 4 are not.
         */
-       switch (2 * weight(p) - nr) {
+       diff = 2 * weight(p) - nr;
+       switch (diff) {
        case -1: case 0: case 1:
                return 1;
        default:
+               /*
+                * For large number of commits we are not so strict, it's
+                * good enough if it's within ~0.1% of the halfway point,
+                * e.g. 5000 is exactly halfway of 10000, but we consider
+                * the values [4996, 5004] as halfway as well.
+                */
+               if (abs(diff) < nr / 1024)
+                       return 1;
                return 0;
        }
 }
@@ -321,8 +332,9 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
                weight_set(p, count_distance(p));
                clear_distance(list);
 
-               /* Does it happen to be at exactly half-way? */
-               if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
+               /* Does it happen to be at half-way? */
+               if (!(bisect_flags & FIND_BISECTION_ALL) &&
+                     approx_halfway(p, nr))
                        return p;
                counted++;
        }
@@ -362,8 +374,9 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
                        else
                                weight_set(p, weight(q));
 
-                       /* Does it happen to be at exactly half-way? */
-                       if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
+                       /* Does it happen to be at half-way? */
+                       if (!(bisect_flags & FIND_BISECTION_ALL) &&
+                             approx_halfway(p, nr))
                                return p;
                }
        }
diff --git a/blame.c b/blame.c
index 9156ebeafd21114e982b52ecde9f50387f869b44..a5044fcfaa62644daa45103dd6ad17647bc6a811 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -435,7 +435,7 @@ static void get_fingerprint(struct fingerprint *result,
 
 static void free_fingerprint(struct fingerprint *f)
 {
-       hashmap_free(&f->map);
+       hashmap_clear(&f->map);
        free(f->entries);
 }
 
index 22b125cf8c122e3a1716f34778fa5595454fe76a..8681031402fb93c0e1d5a865041f6f9a924363c6 100644 (file)
@@ -203,7 +203,7 @@ void blk_SHA1_Init(blk_SHA_CTX *ctx)
        ctx->H[4] = 0xc3d2e1f0;
 }
 
-void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)
 {
        unsigned int lenW = ctx->size & 63;
 
index 4df67477526a2891f7dcdac71ea7bffa75120a67..9fb0441b98849e14e589d7a1b436e3338ae4688d 100644 (file)
@@ -13,7 +13,7 @@ typedef struct {
 } blk_SHA_CTX;
 
 void blk_SHA1_Init(blk_SHA_CTX *ctx);
-void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len);
 void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
 
 #define platform_SHA_CTX       blk_SHA_CTX
diff --git a/bloom.c b/bloom.c
index 68c73200a54aa4fdad582d6031c59313c2c90d73..b176f28f531eccc412204faa5f4da9fa53164081 100644 (file)
--- a/bloom.c
+++ b/bloom.c
@@ -229,10 +229,9 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
        diffcore_std(&diffopt);
 
        if (diff_queued_diff.nr <= settings->max_changed_paths) {
-               struct hashmap pathmap;
+               struct hashmap pathmap = HASHMAP_INIT(pathmap_cmp, NULL);
                struct pathmap_hash_entry *e;
                struct hashmap_iter iter;
-               hashmap_init(&pathmap, pathmap_cmp, NULL, 0);
 
                for (i = 0; i < diff_queued_diff.nr; i++) {
                        const char *path = diff_queued_diff.queue[i]->two->path;
@@ -287,7 +286,7 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
                }
 
        cleanup:
-               hashmap_free_entries(&pathmap, struct pathmap_hash_entry, entry);
+               hashmap_clear_and_free(&pathmap, struct pathmap_hash_entry, entry);
        } else {
                for (i = 0; i < diff_queued_diff.nr; i++)
                        diff_free_filepair(diff_queued_diff.queue[i]);
index 7ac432b88193e7468048821a372dae8c2a81919a..6e18e623fddfbfb33a3de9f4df8cabbbc048ed51 100644 (file)
@@ -342,7 +342,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        const char *workdir, *tmp;
        int ret = 0, i;
        FILE *fp;
-       struct hashmap working_tree_dups, submodules, symlinks2;
+       struct hashmap working_tree_dups = HASHMAP_INIT(working_tree_entry_cmp,
+                                                       NULL);
+       struct hashmap submodules = HASHMAP_INIT(pair_cmp, NULL);
+       struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
        struct hashmap_iter iter;
        struct pair_entry *entry;
        struct index_state wtindex;
@@ -383,10 +386,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        rdir_len = rdir.len;
        wtdir_len = wtdir.len;
 
-       hashmap_init(&working_tree_dups, working_tree_entry_cmp, NULL, 0);
-       hashmap_init(&submodules, pair_cmp, NULL, 0);
-       hashmap_init(&symlinks2, pair_cmp, NULL, 0);
-
        child.no_stdin = 1;
        child.git_cmd = 1;
        child.use_shell = 0;
index f9c3c49f14d9cd508102405f99d6010c21691541..ecf8537605077a8485e59d6dbba6d4d447642910 100644 (file)
@@ -393,7 +393,7 @@ static void find_non_local_tags(const struct ref *refs,
                item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
                string_list_insert(&remote_refs_list, ref->name);
        }
-       hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
+       hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent);
 
        /*
         * We may have a final lightweight tag that needs to be
@@ -428,7 +428,7 @@ static void find_non_local_tags(const struct ref *refs,
                **tail = rm;
                *tail = &rm->next;
        }
-       hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);
+       hashmap_clear_and_free(&remote_refs, struct refname_hash_entry, ent);
        string_list_clear(&remote_refs_list, 0);
        oidset_clear(&fetch_oids);
 }
@@ -573,7 +573,7 @@ static struct ref *get_ref_map(struct remote *remote,
                }
        }
        if (existing_refs_populated)
-               hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
+               hashmap_clear_and_free(&existing_refs, struct refname_hash_entry, ent);
 
        return ref_map;
 }
index 0d03cb442df3fab5a59489a04f3b345aa14a74e4..4b8d86e0adeda0d823b06c7dfebd1909dd514052 100644 (file)
@@ -1597,7 +1597,7 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
 
        /* The address of the 4-byte offset table */
        idx1 = (((const uint32_t *)((const uint8_t *)p->index_data + p->crc_offset))
-               + p->num_objects /* CRC32 table */
+               + (size_t)p->num_objects /* CRC32 table */
                );
 
        /* The address of the 8-byte offset table */
index 49eb8f6431b198d8942d48c77c1cc96e95fd3a0e..08204e3196418217b233387fdf1880af70447ec9 100644 (file)
@@ -37,6 +37,7 @@
 
 #define MAIL_DEFAULT_WRAP 72
 #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
+#define FORMAT_PATCH_NAME_MAX_DEFAULT 64
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -50,6 +51,7 @@ static int decoration_style;
 static int decoration_given;
 static int use_mailmap_config = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
+static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
 static const char *fmt_pretty;
 
 static const char * const builtin_log_usage[] = {
@@ -150,6 +152,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
+       rev->patch_name_max = fmt_patch_name_max;
        rev->show_signature = default_show_signature;
        rev->encode_email_headers = default_encode_email_headers;
        rev->diffopt.flags.allow_textconv = 1;
@@ -457,6 +460,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
                return git_config_string(&fmt_pretty, var, value);
        if (!strcmp(var, "format.subjectprefix"))
                return git_config_string(&fmt_patch_subject_prefix, var, value);
+       if (!strcmp(var, "format.filenamemaxlength")) {
+               fmt_patch_name_max = git_config_int(var, value);
+               return 0;
+       }
        if (!strcmp(var, "format.encodeemailheaders")) {
                default_encode_email_headers = git_config_bool(var, value);
                return 0;
@@ -958,15 +965,9 @@ static int open_next_file(struct commit *commit, const char *subject,
                         struct rev_info *rev, int quiet)
 {
        struct strbuf filename = STRBUF_INIT;
-       int suffix_len = strlen(rev->patch_suffix) + 1;
 
        if (output_directory) {
                strbuf_addstr(&filename, output_directory);
-               if (filename.len >=
-                   PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) {
-                       strbuf_release(&filename);
-                       return error(_("name of output directory is too long"));
-               }
                strbuf_complete(&filename, '/');
        }
 
@@ -1754,6 +1755,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            N_("start numbering patches at <n> instead of 1")),
                OPT_INTEGER('v', "reroll-count", &reroll_count,
                            N_("mark the series as Nth re-roll")),
+               OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
+                           N_("max length of output filename")),
                OPT_CALLBACK_F(0, "rfc", &rev, NULL,
                            N_("Use [RFC PATCH] instead of [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback),
@@ -1854,6 +1857,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
                             PARSE_OPT_KEEP_DASHDASH);
 
+       /* Make sure "0000-$sub.patch" gives non-negative length for $sub */
+       if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
+               fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
+
        if (cover_from_description_arg)
                cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
@@ -1938,6 +1945,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
        rev.zero_commit = zero_commit;
+       rev.patch_name_max = fmt_patch_name_max;
 
        if (!rev.diffopt.flags.text && !no_binary_diff)
                rev.diffopt.flags.binary = 1;
index 178e3409b7f8e0477e1a41e3575bcf886c6d664b..3e70f2a4c1f0fe370bd8a1b4d1e5deba31284a48 100644 (file)
@@ -236,7 +236,7 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
 
 static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
 {
-       unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
+       size_t p1_off = 0, p2_off = 0, p1_step, p2_step;
        const unsigned char *p1_base, *p2_base;
        struct llist_item *p1_hint = NULL, *p2_hint = NULL;
        const unsigned int hashsz = the_hash_algo->rawsz;
@@ -280,7 +280,7 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
 static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
 {
        size_t ret = 0;
-       unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
+       size_t p1_off = 0, p2_off = 0, p1_step, p2_step;
        const unsigned char *p1_base, *p2_base;
        const unsigned int hashsz = the_hash_algo->rawsz;
 
@@ -499,7 +499,7 @@ static void scan_alt_odb_packs(void)
 static struct pack_list * add_pack(struct packed_git *p)
 {
        struct pack_list l;
-       unsigned long off = 0, step;
+       size_t off = 0, step;
        const unsigned char *base;
 
        if (!p->pack_local && !(alt_odb || verbose))
index bb9909c52e4e01e0ab37dbd020138ba254d44b51..f1f0f7bef670d0867c610789c23fd7176e4770b0 100644 (file)
@@ -977,15 +977,25 @@ static int read_proc_receive_report(struct packet_reader *reader,
        int new_report = 0;
        int code = 0;
        int once = 0;
+       int response = 0;
 
        for (;;) {
                struct object_id old_oid, new_oid;
                const char *head;
                const char *refname;
                char *p;
-
-               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+               enum packet_read_status status;
+
+               status = packet_reader_read(reader);
+               if (status != PACKET_READ_NORMAL) {
+                       /* Check whether proc-receive exited abnormally */
+                       if (status == PACKET_READ_EOF && !response) {
+                               strbuf_addstr(errmsg, "proc-receive exited abnormally");
+                               return -1;
+                       }
                        break;
+               }
+               response++;
 
                head = reader->line;
                p = strchr(head, ' ');
@@ -1145,31 +1155,49 @@ static int run_proc_receive_hook(struct command *commands,
        if (use_push_options)
                strbuf_addstr(&cap, " push-options");
        if (cap.len) {
-               packet_write_fmt(proc.in, "version=1%c%s\n", '\0', cap.buf + 1);
+               code = packet_write_fmt_gently(proc.in, "version=1%c%s\n", '\0', cap.buf + 1);
                strbuf_release(&cap);
        } else {
-               packet_write_fmt(proc.in, "version=1\n");
+               code = packet_write_fmt_gently(proc.in, "version=1\n");
        }
-       packet_flush(proc.in);
+       if (!code)
+               code = packet_flush_gently(proc.in);
 
-       for (;;) {
-               int linelen;
+       if (!code)
+               for (;;) {
+                       int linelen;
+                       enum packet_read_status status;
 
-               if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
-                       break;
+                       status = packet_reader_read(&reader);
+                       if (status != PACKET_READ_NORMAL) {
+                               /* Check whether proc-receive exited abnormally */
+                               if (status == PACKET_READ_EOF)
+                                       code = -1;
+                               break;
+                       }
 
-               if (reader.pktlen > 8 && starts_with(reader.line, "version=")) {
-                       version = atoi(reader.line + 8);
-                       linelen = strlen(reader.line);
-                       if (linelen < reader.pktlen) {
-                               const char *feature_list = reader.line + linelen + 1;
-                               if (parse_feature_request(feature_list, "push-options"))
-                                       hook_use_push_options = 1;
+                       if (reader.pktlen > 8 && starts_with(reader.line, "version=")) {
+                               version = atoi(reader.line + 8);
+                               linelen = strlen(reader.line);
+                               if (linelen < reader.pktlen) {
+                                       const char *feature_list = reader.line + linelen + 1;
+                                       if (parse_feature_request(feature_list, "push-options"))
+                                               hook_use_push_options = 1;
+                               }
                        }
                }
+
+       if (code) {
+               strbuf_addstr(&errmsg, "fail to negotiate version with proc-receive hook");
+               goto cleanup;
        }
 
-       if (version != 1) {
+       switch (version) {
+       case 0:
+               /* fallthrough */
+       case 1:
+               break;
+       default:
                strbuf_addf(&errmsg, "proc-receive version '%d' is not supported",
                            version);
                code = -1;
@@ -1180,20 +1208,36 @@ static int run_proc_receive_hook(struct command *commands,
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->run_proc_receive || cmd->skip_update || cmd->error_string)
                        continue;
-               packet_write_fmt(proc.in, "%s %s %s",
-                                oid_to_hex(&cmd->old_oid),
-                                oid_to_hex(&cmd->new_oid),
-                                cmd->ref_name);
+               code = packet_write_fmt_gently(proc.in, "%s %s %s",
+                                              oid_to_hex(&cmd->old_oid),
+                                              oid_to_hex(&cmd->new_oid),
+                                              cmd->ref_name);
+               if (code)
+                       break;
+       }
+       if (!code)
+               code = packet_flush_gently(proc.in);
+       if (code) {
+               strbuf_addstr(&errmsg, "fail to write commands to proc-receive hook");
+               goto cleanup;
        }
-       packet_flush(proc.in);
 
        /* Send push options */
        if (hook_use_push_options) {
                struct string_list_item *item;
 
-               for_each_string_list_item(item, push_options)
-                       packet_write_fmt(proc.in, "%s", item->string);
-               packet_flush(proc.in);
+               for_each_string_list_item(item, push_options) {
+                       code = packet_write_fmt_gently(proc.in, "%s", item->string);
+                       if (code)
+                               break;
+               }
+               if (!code)
+                       code = packet_flush_gently(proc.in);
+               if (code) {
+                       strbuf_addstr(&errmsg,
+                                     "fail to write push-options to proc-receive hook");
+                       goto cleanup;
+               }
        }
 
        /* Read result from proc-receive */
index ed200c8af1285ed4ac45a50aaa6275a739585836..69ba7326cf823a774d3d673a283cfe08a0fce3fc 100644 (file)
@@ -595,6 +595,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        struct object_context unused;
        struct strbuf buf = STRBUF_INIT;
        const int hexsz = the_hash_algo->hexsz;
+       int seen_end_of_options = 0;
 
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
                return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -622,21 +623,29 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
-               if (!strcmp(arg, "--local-env-vars")) {
-                       int i;
-                       for (i = 0; local_repo_env[i]; i++)
-                               printf("%s\n", local_repo_env[i]);
+               if (as_is) {
+                       if (show_file(arg, output_prefix) && as_is < 2)
+                               verify_filename(prefix, arg, 0);
                        continue;
                }
-               if (!strcmp(arg, "--resolve-git-dir")) {
-                       const char *gitdir = argv[++i];
-                       if (!gitdir)
-                               die("--resolve-git-dir requires an argument");
-                       gitdir = resolve_gitdir(gitdir);
-                       if (!gitdir)
-                               die("not a gitdir '%s'", argv[i]);
-                       puts(gitdir);
-                       continue;
+
+               if (!seen_end_of_options) {
+                       if (!strcmp(arg, "--local-env-vars")) {
+                               int i;
+                               for (i = 0; local_repo_env[i]; i++)
+                                       printf("%s\n", local_repo_env[i]);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--resolve-git-dir")) {
+                               const char *gitdir = argv[++i];
+                               if (!gitdir)
+                                       die("--resolve-git-dir requires an argument");
+                               gitdir = resolve_gitdir(gitdir);
+                               if (!gitdir)
+                                       die("not a gitdir '%s'", argv[i]);
+                               puts(gitdir);
+                               continue;
+                       }
                }
 
                /* The rest of the options require a git repository. */
@@ -646,41 +655,36 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        did_repo_setup = 1;
                }
 
-               if (!strcmp(arg, "--git-path")) {
-                       if (!argv[i + 1])
-                               die("--git-path requires an argument");
-                       strbuf_reset(&buf);
-                       puts(relative_path(git_path("%s", argv[i + 1]),
-                                          prefix, &buf));
-                       i++;
-                       continue;
-               }
-               if (as_is) {
-                       if (show_file(arg, output_prefix) && as_is < 2)
-                               verify_filename(prefix, arg, 0);
-                       continue;
-               }
-               if (!strcmp(arg,"-n")) {
-                       if (++i >= argc)
-                               die("-n requires an argument");
-                       if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
-                               show(arg);
-                               show(argv[i]);
-                       }
-                       continue;
-               }
-               if (starts_with(arg, "-n")) {
-                       if ((filter & DO_FLAGS) && (filter & DO_REVS))
-                               show(arg);
+               if (!strcmp(arg, "--")) {
+                       as_is = 2;
+                       /* Pass on the "--" if we show anything but files.. */
+                       if (filter & (DO_FLAGS | DO_REVS))
+                               show_file(arg, 0);
                        continue;
                }
 
-               if (*arg == '-') {
-                       if (!strcmp(arg, "--")) {
-                               as_is = 2;
-                               /* Pass on the "--" if we show anything but files.. */
-                               if (filter & (DO_FLAGS | DO_REVS))
-                                       show_file(arg, 0);
+               if (!seen_end_of_options && *arg == '-') {
+                       if (!strcmp(arg, "--git-path")) {
+                               if (!argv[i + 1])
+                                       die("--git-path requires an argument");
+                               strbuf_reset(&buf);
+                               puts(relative_path(git_path("%s", argv[i + 1]),
+                                                  prefix, &buf));
+                               i++;
+                               continue;
+                       }
+                       if (!strcmp(arg,"-n")) {
+                               if (++i >= argc)
+                                       die("-n requires an argument");
+                               if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
+                                       show(arg);
+                                       show(argv[i]);
+                               }
+                               continue;
+                       }
+                       if (starts_with(arg, "-n")) {
+                               if ((filter & DO_FLAGS) && (filter & DO_REVS))
+                                       show(arg);
                                continue;
                        }
                        if (!strcmp(arg, "--default")) {
@@ -937,6 +941,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                puts(the_hash_algo->name);
                                continue;
                        }
+                       if (!strcmp(arg, "--end-of-options")) {
+                               seen_end_of_options = 1;
+                               if (filter & (DO_FLAGS | DO_REVS))
+                                       show_file(arg, 0);
+                               continue;
+                       }
                        if (show_flag(arg) && verify)
                                die_no_single_rev(quiet);
                        continue;
index 0a5c4968f64e1655b5cf16e3555209d52dae065c..c52e4ccd19a224fd14817c640a7b740a31954479 100644 (file)
@@ -10,6 +10,7 @@
 #include "shortlog.h"
 #include "parse-options.h"
 #include "trailer.h"
+#include "strmap.h"
 
 static char const * const shortlog_usage[] = {
        N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"),
@@ -169,60 +170,6 @@ static void read_from_stdin(struct shortlog *log)
        strbuf_release(&oneline);
 }
 
-struct strset_item {
-       struct hashmap_entry ent;
-       char value[FLEX_ARRAY];
-};
-
-struct strset {
-       struct hashmap map;
-};
-
-#define STRSET_INIT { { NULL } }
-
-static int strset_item_hashcmp(const void *hash_data,
-                              const struct hashmap_entry *entry,
-                              const struct hashmap_entry *entry_or_key,
-                              const void *keydata)
-{
-       const struct strset_item *a, *b;
-
-       a = container_of(entry, const struct strset_item, ent);
-       if (keydata)
-               return strcmp(a->value, keydata);
-
-       b = container_of(entry_or_key, const struct strset_item, ent);
-       return strcmp(a->value, b->value);
-}
-
-/*
- * Adds "str" to the set if it was not already present; returns true if it was
- * already there.
- */
-static int strset_check_and_add(struct strset *ss, const char *str)
-{
-       unsigned int hash = strhash(str);
-       struct strset_item *item;
-
-       if (!ss->map.table)
-               hashmap_init(&ss->map, strset_item_hashcmp, NULL, 0);
-
-       if (hashmap_get_from_hash(&ss->map, hash, str))
-               return 1;
-
-       FLEX_ALLOC_STR(item, value, str);
-       hashmap_entry_init(&item->ent, hash);
-       hashmap_add(&ss->map, &item->ent);
-       return 0;
-}
-
-static void strset_clear(struct strset *ss)
-{
-       if (!ss->map.table)
-               return;
-       hashmap_free_entries(&ss->map, struct strset_item, ent);
-}
-
 static void insert_records_from_trailers(struct shortlog *log,
                                         struct strset *dups,
                                         struct commit *commit,
@@ -253,7 +200,7 @@ static void insert_records_from_trailers(struct shortlog *log,
                if (!parse_ident(log, &ident, value))
                        value = ident.buf;
 
-               if (strset_check_and_add(dups, value))
+               if (!strset_add(dups, value))
                        continue;
                insert_one_record(log, value, oneline);
        }
@@ -291,7 +238,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                                      log->email ? "%aN <%aE>" : "%aN",
                                      &ident, &ctx);
                if (!HAS_MULTI_BITS(log->groups) ||
-                   !strset_check_and_add(&dups, ident.buf))
+                   strset_add(&dups, ident.buf))
                        insert_one_record(log, ident.buf, oneline_str);
        }
        if (log->groups & SHORTLOG_GROUP_COMMITTER) {
@@ -300,7 +247,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                                      log->email ? "%cN <%cE>" : "%cN",
                                      &ident, &ctx);
                if (!HAS_MULTI_BITS(log->groups) ||
-                   !strset_check_and_add(&dups, ident.buf))
+                   strset_add(&dups, ident.buf))
                        insert_one_record(log, ident.buf, oneline_str);
        }
        if (log->groups & SHORTLOG_GROUP_TRAILER) {
index 92a983a265c246da0865fbd9f82ae5c15bb99322..c70d6cdbf243db05f9e5718070895abd837d617a 100755 (executable)
@@ -48,7 +48,7 @@ do
                        ;;
                github-actions)
                        mkdir -p failed-test-artifacts
-                       echo "::set-env name=FAILED_TEST_ARTIFACTS::t/failed-test-artifacts"
+                       echo "FAILED_TEST_ARTIFACTS=t/failed-test-artifacts" >>$GITHUB_ENV
                        cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/
                        tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
                        continue
index 2bdff4457be7d5b1e1fd5f6acb558ac41b946a8c..8f324ed3a6f6b8d516516e729afe627610051c22 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1963,7 +1963,7 @@ void git_configset_clear(struct config_set *cs)
                free(entry->key);
                string_list_clear(&entry->value_list, 1);
        }
-       hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
+       hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
        cs->hash_initialized = 0;
        free(cs->list.items);
        cs->list.nr = 0;
index c7eba69e54e669c61dd8b7333d97fb587964b68c..5b30a9154acea040bbf4aec179edf6c37737e5fc 100644 (file)
@@ -541,11 +541,6 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        # removing the directory at OS releases J06.21 and L17.02.
        # Default to the older rm until those two releases are deprecated.
        RM = /bin/rm -f
-       # As detected by './configure'.
-       # Missdetected, hence commented out, see below.
-       #NO_CURL = YesPlease
-       # Added manually, see above.
-       NEEDS_SSL_WITH_CURL = YesPlease
        NEEDS_CRYPTO_WITH_SSL = YesPlease
        HAVE_DEV_TTY = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
@@ -636,7 +631,6 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        prefix =
        INSTALL = /bin/install
        EXTLIBS += /mingw/lib/libz.a
-       NO_R_TO_GCC_LINKER = YesPlease
        INTERNAL_QSORT = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
        NO_GETTEXT = YesPlease
@@ -669,7 +663,6 @@ else
                        -fstack-protector-strong
                EXTLIBS += -lntdll
                INSTALL = /bin/install
-               NO_R_TO_GCC_LINKER = YesPlease
                INTERNAL_QSORT = YesPlease
                HAVE_LIBCHARSET_H = YesPlease
                NO_GETTEXT =
@@ -695,7 +688,6 @@ ifeq ($(uname_S),QNX)
        NO_MKDTEMP = YesPlease
        NO_NSEC = YesPlease
        NO_PTHREADS = YesPlease
-       NO_R_TO_GCC_LINKER = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
 endif
index 7c81e4ba497fb6ab1b9b32a40336f5995b0b5b55..1ed03623cd754b1182ed7f6b3d58f9e913c18b7b 100644 (file)
@@ -1120,26 +1120,44 @@ __git_pretty_aliases ()
 # __git_aliased_command requires 1 argument
 __git_aliased_command ()
 {
-       local word cmdline=$(__git config --get "alias.$1")
-       for word in $cmdline; do
-               case "$word" in
-               \!gitk|gitk)
-                       echo "gitk"
-                       return
-                       ;;
-               \!*)    : shell command alias ;;
-               -*)     : option ;;
-               *=*)    : setting env ;;
-               git)    : git itself ;;
-               \(\))   : skip parens of shell function definition ;;
-               {)      : skip start of shell helper function ;;
-               :)      : skip null command ;;
-               \'*)    : skip opening quote after sh -c ;;
-               *)
-                       echo "$word"
+       local cur=$1 last list word cmdline
+
+       while [[ -n "$cur" ]]; do
+               if [[ "$list" == *" $cur "* ]]; then
+                       # loop detected
                        return
-               esac
+               fi
+
+               cmdline=$(__git config --get "alias.$cur")
+               list=" $cur $list"
+               last=$cur
+               cur=
+
+               for word in $cmdline; do
+                       case "$word" in
+                       \!gitk|gitk)
+                               cur="gitk"
+                               break
+                               ;;
+                       \!*)    : shell command alias ;;
+                       -*)     : option ;;
+                       *=*)    : setting env ;;
+                       git)    : git itself ;;
+                       \(\))   : skip parens of shell function definition ;;
+                       {)      : skip start of shell helper function ;;
+                       :)      : skip null command ;;
+                       \'*)    : skip opening quote after sh -c ;;
+                       *)
+                               cur="$word"
+                               break
+                       esac
+               done
        done
+
+       cur=$last
+       if [[ "$cur" != "$1" ]]; then
+               echo "$cur"
+       fi
 }
 
 # Check whether one of the given words is present on the command line,
index f9cbd317fbbce89547a405783fb5cc69e7815efa..e54d53d1d0b3537b4fb4b0f22f9699cf38a2c3cf 100644 (file)
@@ -62,4 +62,11 @@ static inline void hashwrite_be32(struct hashfile *f, uint32_t data)
        hashwrite(f, &data, sizeof(data));
 }
 
+static inline size_t hashwrite_be64(struct hashfile *f, uint64_t data)
+{
+       data = htonll(data);
+       hashwrite(f, &data, sizeof(data));
+       return sizeof(data);
+}
+
 #endif
diff --git a/diff.c b/diff.c
index d24f47df993ead73df360571a6ad9cbac604287f..643f4f3f6d02583ba74b81a5454ec89a79784161 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4115,6 +4115,9 @@ void diff_free_filespec_blob(struct diff_filespec *s)
 
 void diff_free_filespec_data(struct diff_filespec *s)
 {
+       if (!s)
+               return;
+
        diff_free_filespec_blob(s);
        FREE_AND_NULL(s->cnt_data);
 }
@@ -6312,9 +6315,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
                        if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
                                dim_moved_lines(o);
 
-                       hashmap_free_entries(&add_lines, struct moved_entry,
+                       hashmap_clear_and_free(&add_lines, struct moved_entry,
                                                ent);
-                       hashmap_free_entries(&del_lines, struct moved_entry,
+                       hashmap_clear_and_free(&del_lines, struct moved_entry,
                                                ent);
                }
 
index 99e63e90f89afaf55ef16bb3c11c1546c4d76c2a..d367a6d24434727fd822754436db50d3fc277764 100644 (file)
@@ -407,7 +407,7 @@ static int find_exact_renames(struct diff_options *options)
                renames += find_identical_files(&file_table, i, options);
 
        /* Free the hash data structure and entries */
-       hashmap_free_entries(&file_table, struct file_similarity, entry);
+       hashmap_clear_and_free(&file_table, struct file_similarity, entry);
 
        return renames;
 }
diff --git a/dir.c b/dir.c
index ebea5f1f91439a097e48ffb5983fbff2bc0ff918..d637461da5cb9a03f86fe06d7524b756045755ac 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -817,8 +817,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
 
 clear_hashmaps:
        warning(_("disabling cone pattern matching"));
-       hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
-       hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
+       hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
+       hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
        pl->use_cone_patterns = 0;
 }
 
@@ -921,8 +921,8 @@ void clear_pattern_list(struct pattern_list *pl)
                free(pl->patterns[i]);
        free(pl->patterns);
        free(pl->filebuf);
-       hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
-       hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
+       hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
+       hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
 
        memset(pl, 0, sizeof(*pl));
 }
index 2defef28cd931fc9dca041ee7ec0ea5d440c3dd7..7225abd81122e6bd798989d6e21fae1279cf3929 100644 (file)
@@ -138,6 +138,10 @@ setup_user_tool () {
        merge_cmd () {
                ( eval $merge_tool_cmd )
        }
+
+       list_tool_variants () {
+               echo "$tool"
+       }
 }
 
 setup_tool () {
index 09813e1a46676c1475c1ad903f1d9358bf56827e..5009471800e8c61b50bfd670a7714daaa26a01c7 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -92,8 +92,9 @@ static void alloc_table(struct hashmap *map, unsigned int size)
 }
 
 static inline int entry_equals(const struct hashmap *map,
-               const struct hashmap_entry *e1, const struct hashmap_entry *e2,
-               const void *keydata)
+                              const struct hashmap_entry *e1,
+                              const struct hashmap_entry *e2,
+                              const void *keydata)
 {
        return (e1 == e2) ||
               (e1->hash == e2->hash &&
@@ -101,7 +102,7 @@ static inline int entry_equals(const struct hashmap *map,
 }
 
 static inline unsigned int bucket(const struct hashmap *map,
-               const struct hashmap_entry *key)
+                                 const struct hashmap_entry *key)
 {
        return key->hash & (map->tablesize - 1);
 }
@@ -113,6 +114,7 @@ int hashmap_bucket(const struct hashmap *map, unsigned int hash)
 
 static void rehash(struct hashmap *map, unsigned int newsize)
 {
+       /* map->table MUST NOT be NULL when this function is called */
        unsigned int i, oldsize = map->tablesize;
        struct hashmap_entry **oldtable = map->table;
 
@@ -133,6 +135,7 @@ static void rehash(struct hashmap *map, unsigned int newsize)
 static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
                const struct hashmap_entry *key, const void *keydata)
 {
+       /* map->table MUST NOT be NULL when this function is called */
        struct hashmap_entry **e = &map->table[bucket(map, key)];
        while (*e && !entry_equals(map, *e, key, keydata))
                e = &(*e)->next;
@@ -148,7 +151,7 @@ static int always_equal(const void *unused_cmp_data,
 }
 
 void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
-               const void *cmpfn_data, size_t initial_size)
+                 const void *cmpfn_data, size_t initial_size)
 {
        unsigned int size = HASHMAP_INITIAL_SIZE;
 
@@ -171,22 +174,37 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
        map->do_count_items = 1;
 }
 
-void hashmap_free_(struct hashmap *map, ssize_t entry_offset)
+static void free_individual_entries(struct hashmap *map, ssize_t entry_offset)
+{
+       struct hashmap_iter iter;
+       struct hashmap_entry *e;
+
+       hashmap_iter_init(map, &iter);
+       while ((e = hashmap_iter_next(&iter)))
+               /*
+                * like container_of, but using caller-calculated
+                * offset (caller being hashmap_clear_and_free)
+                */
+               free((char *)e - entry_offset);
+}
+
+void hashmap_partial_clear_(struct hashmap *map, ssize_t entry_offset)
 {
        if (!map || !map->table)
                return;
-       if (entry_offset >= 0) { /* called by hashmap_free_entries */
-               struct hashmap_iter iter;
-               struct hashmap_entry *e;
-
-               hashmap_iter_init(map, &iter);
-               while ((e = hashmap_iter_next(&iter)))
-                       /*
-                        * like container_of, but using caller-calculated
-                        * offset (caller being hashmap_free_entries)
-                        */
-                       free((char *)e - entry_offset);
-       }
+       if (entry_offset >= 0)  /* called by hashmap_clear_entries */
+               free_individual_entries(map, entry_offset);
+       memset(map->table, 0, map->tablesize * sizeof(struct hashmap_entry *));
+       map->shrink_at = 0;
+       map->private_size = 0;
+}
+
+void hashmap_clear_(struct hashmap *map, ssize_t entry_offset)
+{
+       if (!map || !map->table)
+               return;
+       if (entry_offset >= 0)  /* called by hashmap_clear_and_free */
+               free_individual_entries(map, entry_offset);
        free(map->table);
        memset(map, 0, sizeof(*map));
 }
@@ -195,11 +213,13 @@ struct hashmap_entry *hashmap_get(const struct hashmap *map,
                                const struct hashmap_entry *key,
                                const void *keydata)
 {
+       if (!map->table)
+               return NULL;
        return *find_entry_ptr(map, key, keydata);
 }
 
 struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
-                       const struct hashmap_entry *entry)
+                                      const struct hashmap_entry *entry)
 {
        struct hashmap_entry *e = entry->next;
        for (; e; e = e->next)
@@ -210,8 +230,12 @@ struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
 
 void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
 {
-       unsigned int b = bucket(map, entry);
+       unsigned int b;
+
+       if (!map->table)
+               alloc_table(map, HASHMAP_INITIAL_SIZE);
 
+       b = bucket(map, entry);
        /* add entry */
        entry->next = map->table[b];
        map->table[b] = entry;
@@ -225,11 +249,15 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
 }
 
 struct hashmap_entry *hashmap_remove(struct hashmap *map,
-                                       const struct hashmap_entry *key,
-                                       const void *keydata)
+                                    const struct hashmap_entry *key,
+                                    const void *keydata)
 {
        struct hashmap_entry *old;
-       struct hashmap_entry **e = find_entry_ptr(map, key, keydata);
+       struct hashmap_entry **e;
+
+       if (!map->table)
+               return NULL;
+       e = find_entry_ptr(map, key, keydata);
        if (!*e)
                return NULL;
 
@@ -249,7 +277,7 @@ struct hashmap_entry *hashmap_remove(struct hashmap *map,
 }
 
 struct hashmap_entry *hashmap_put(struct hashmap *map,
-                               struct hashmap_entry *entry)
+                                 struct hashmap_entry *entry)
 {
        struct hashmap_entry *old = hashmap_remove(map, entry, NULL);
        hashmap_add(map, entry);
index b011b394fefe34f46d098f02f1e19fd987bb2476..7251687d730d608437a8ed24e4551cf9344fd7f4 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -96,7 +96,7 @@
  *         }
  *
  *         if (!strcmp("end", action)) {
- *             hashmap_free_entries(&map, struct long2string, ent);
+ *             hashmap_clear_and_free(&map, struct long2string, ent);
  *             break;
  *         }
  *     }
@@ -210,6 +210,9 @@ struct hashmap {
 
 /* hashmap functions */
 
+#define HASHMAP_INIT(fn, data) { .cmpfn = fn, .cmpfn_data = data, \
+                                .do_count_items = 1 }
+
 /*
  * Initializes a hashmap structure.
  *
@@ -228,24 +231,72 @@ struct hashmap {
  * prevent expensive resizing. If 0, the table is dynamically resized.
  */
 void hashmap_init(struct hashmap *map,
-                        hashmap_cmp_fn equals_function,
-                        const void *equals_function_data,
-                        size_t initial_size);
+                 hashmap_cmp_fn equals_function,
+                 const void *equals_function_data,
+                 size_t initial_size);
 
-/* internal function for freeing hashmap */
-void hashmap_free_(struct hashmap *map, ssize_t offset);
+/* internal functions for clearing or freeing hashmap */
+void hashmap_partial_clear_(struct hashmap *map, ssize_t offset);
+void hashmap_clear_(struct hashmap *map, ssize_t offset);
 
 /*
- * Frees a hashmap structure and allocated memory, leaves entries undisturbed
+ * Frees a hashmap structure and allocated memory for the table, but does not
+ * free the entries nor anything they point to.
+ *
+ * Usage note:
+ *
+ * Many callers will need to iterate over all entries and free the data each
+ * entry points to; in such a case, they can free the entry itself while at it.
+ * Thus, you might see:
+ *
+ *    hashmap_for_each_entry(map, hashmap_iter, e, hashmap_entry_name) {
+ *      free(e->somefield);
+ *      free(e);
+ *    }
+ *    hashmap_clear(map);
+ *
+ * instead of
+ *
+ *    hashmap_for_each_entry(map, hashmap_iter, e, hashmap_entry_name) {
+ *      free(e->somefield);
+ *    }
+ *    hashmap_clear_and_free(map, struct my_entry_struct, hashmap_entry_name);
+ *
+ * to avoid the implicit extra loop over the entries.  However, if there are
+ * no special fields in your entry that need to be freed beyond the entry
+ * itself, it is probably simpler to avoid the explicit loop and just call
+ * hashmap_clear_and_free().
  */
-#define hashmap_free(map) hashmap_free_(map, -1)
+#define hashmap_clear(map) hashmap_clear_(map, -1)
 
 /*
- * Frees @map and all entries.  @type is the struct type of the entry
- * where @member is the hashmap_entry struct used to associate with @map
+ * Similar to hashmap_clear(), except that the table is no deallocated; it
+ * is merely zeroed out but left the same size as before.  If the hashmap
+ * will be reused, this avoids the overhead of deallocating and
+ * reallocating map->table.  As with hashmap_clear(), you may need to free
+ * the entries yourself before calling this function.
+ */
+#define hashmap_partial_clear(map) hashmap_partial_clear_(map, -1)
+
+/*
+ * Similar to hashmap_clear() but also frees all entries.  @type is the
+ * struct type of the entry where @member is the hashmap_entry struct used
+ * to associate with @map.
+ *
+ * See usage note above hashmap_clear().
+ */
+#define hashmap_clear_and_free(map, type, member) \
+       hashmap_clear_(map, offsetof(type, member))
+
+/*
+ * Similar to hashmap_partial_clear() but also frees all entries.  @type is
+ * the struct type of the entry where @member is the hashmap_entry struct
+ * used to associate with @map.
+ *
+ * See usage note above hashmap_clear().
  */
-#define hashmap_free_entries(map, type, member) \
-       hashmap_free_(map, offsetof(type, member));
+#define hashmap_partial_clear_and_free(map, type, member) \
+       hashmap_partial_clear_(map, offsetof(type, member))
 
 /* hashmap_entry functions */
 
@@ -261,7 +312,7 @@ void hashmap_free_(struct hashmap *map, ssize_t offset);
  * and if it is on stack, you can just let it go out of scope).
  */
 static inline void hashmap_entry_init(struct hashmap_entry *e,
-                                       unsigned int hash)
+                                     unsigned int hash)
 {
        e->hash = hash;
        e->next = NULL;
@@ -303,8 +354,8 @@ static inline unsigned int hashmap_get_size(struct hashmap *map)
  * to `hashmap_cmp_fn` to decide whether the entry matches the key.
  */
 struct hashmap_entry *hashmap_get(const struct hashmap *map,
-                               const struct hashmap_entry *key,
-                               const void *keydata);
+                                 const struct hashmap_entry *key,
+                                 const void *keydata);
 
 /*
  * Returns the hashmap entry for the specified hash code and key data,
@@ -337,7 +388,7 @@ static inline struct hashmap_entry *hashmap_get_from_hash(
  * call to `hashmap_get` or `hashmap_get_next`.
  */
 struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
-                       const struct hashmap_entry *entry);
+                                      const struct hashmap_entry *entry);
 
 /*
  * Adds a hashmap entry. This allows to add duplicate entries (i.e.
@@ -357,7 +408,7 @@ void hashmap_add(struct hashmap *map, struct hashmap_entry *entry);
  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).
  */
 struct hashmap_entry *hashmap_put(struct hashmap *map,
-                               struct hashmap_entry *entry);
+                                 struct hashmap_entry *entry);
 
 /*
  * Adds or replaces a hashmap entry contained within @keyvar,
@@ -379,8 +430,8 @@ struct hashmap_entry *hashmap_put(struct hashmap *map,
  * Argument explanation is the same as in `hashmap_get`.
  */
 struct hashmap_entry *hashmap_remove(struct hashmap *map,
-                                       const struct hashmap_entry *key,
-                                       const void *keydata);
+                                    const struct hashmap_entry *key,
+                                    const void *keydata);
 
 /*
  * Removes a hashmap entry contained within @keyvar,
@@ -422,7 +473,7 @@ struct hashmap_entry *hashmap_iter_next(struct hashmap_iter *iter);
 
 /* Initializes the iterator and returns the first entry, if any. */
 static inline struct hashmap_entry *hashmap_iter_first(struct hashmap *map,
-               struct hashmap_iter *iter)
+                                                      struct hashmap_iter *iter)
 {
        hashmap_iter_init(map, iter);
        return hashmap_iter_next(iter);
index defd3dfd101f414c5708dc576ff4b7fac8a058a3..d2d1c81caf33fdc12eea412dd428bfe80ee36103 100644 (file)
@@ -35,7 +35,7 @@ const char *list_object_filter_config_name(enum list_objects_filter_choice c)
                /* not a real filter type; just the count of all filters */
                break;
        }
-       BUG("list_object_filter_choice_name: invalid argument '%d'", c);
+       BUG("list_object_filter_config_name: invalid argument '%d'", c);
 }
 
 /*
index 1927f917ce94ce2ec95c889754d6a86177fa2cac..fd0dde97ec324ff6611fa5a7685d4b2831350b3c 100644 (file)
@@ -367,7 +367,7 @@ void fmt_output_subject(struct strbuf *filename,
        const char *suffix = info->patch_suffix;
        int nr = info->nr;
        int start_len = filename->len;
-       int max_len = start_len + FORMAT_PATCH_NAME_MAX - (strlen(suffix) + 1);
+       int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
        if (0 < info->reroll_count)
                strbuf_addf(filename, "v%d-", info->reroll_count);
index 8fa79289ec6b6cb27e68534845d345caf40137ea..1e8c91dbf21f365efcb2b4e966ba6e1cff23ad95 100644 (file)
@@ -33,7 +33,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             int maybe_multipart);
 void load_ref_decorations(struct decoration_filter *filter, int flags);
 
-#define FORMAT_PATCH_NAME_MAX 64
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
 void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *);
 void fmt_output_email_subject(struct strbuf *, struct rev_info *);
index d0214335a79ddef7202e0552b80111a05c36f14c..f736a0f63234fee610b42abdf2d7458d932e1d70 100644 (file)
@@ -2651,7 +2651,7 @@ static struct string_list *get_renames(struct merge_options *opt,
                free(e->target_file);
                string_list_clear(&e->source_files, 0);
        }
-       hashmap_free_entries(&collisions, struct collision_entry, ent);
+       hashmap_clear_and_free(&collisions, struct collision_entry, ent);
        return renames;
 }
 
@@ -2870,7 +2870,7 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
                strbuf_release(&e->new_dir);
                /* possible_new_dirs already cleared in get_directory_renames */
        }
-       hashmap_free_entries(dir_renames, struct dir_rename_entry, ent);
+       hashmap_clear_and_free(dir_renames, struct dir_rename_entry, ent);
        free(dir_renames);
 
        free(pairs->queue);
@@ -3497,7 +3497,7 @@ static int merge_trees_internal(struct merge_options *opt,
                string_list_clear(entries, 1);
                free(entries);
 
-               hashmap_free_entries(&opt->priv->current_file_dir_set,
+               hashmap_clear_and_free(&opt->priv->current_file_dir_set,
                                        struct path_hashmap_entry, e);
 
                if (clean < 0) {
index a89086ee720f842359eefe58f369af2774457438..26c19d46a5bdee5739b9a9425cc48fac3efd6bfb 100644 (file)
@@ -25,4 +25,5 @@ translate_merge_tool_path() {
 list_tool_variants () {
        echo bc
        echo bc3
+       echo bc4
 }
diff --git a/midx.c b/midx.c
index d233b54ac71a0a81fea31ab0286025802a3ed240..da03c1449aa119b03a34605b72056b8727318eb6 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -785,9 +785,7 @@ static size_t write_midx_large_offsets(struct hashfile *f, uint32_t nr_large_off
                if (!(offset >> 31))
                        continue;
 
-               hashwrite_be32(f, offset >> 32);
-               hashwrite_be32(f, offset & 0xffffffffUL);
-               written += 2 * sizeof(uint32_t);
+               written += hashwrite_be64(f, offset);
 
                nr_large_offset--;
        }
@@ -975,8 +973,7 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
                            chunk_offsets[i]);
 
                hashwrite_be32(f, chunk_ids[i]);
-               hashwrite_be32(f, chunk_offsets[i] >> 32);
-               hashwrite_be32(f, chunk_offsets[i]);
+               hashwrite_be64(f, chunk_offsets[i]);
 
                written += MIDX_CHUNKLOOKUP_WIDTH;
        }
index fb526a3775f69eb02d6b7e47629bf60a8a0153fa..5d3c7b12c1805cad19e8cef165e79e232d0f4de4 100644 (file)
@@ -726,6 +726,6 @@ void free_name_hash(struct index_state *istate)
                return;
        istate->name_hash_initialized = 0;
 
-       hashmap_free(&istate->name_hash);
-       hashmap_free_entries(&istate->dir_hash, struct dir_entry, ent);
+       hashmap_clear(&istate->name_hash);
+       hashmap_clear_and_free(&istate->dir_hash, struct dir_entry, ent);
 }
index 05544bc92ba8e9751a773d489cf82ba4a936194d..68f80b0b3d83e09f46bf3875c45647cee79772e8 100644 (file)
--- a/object.c
+++ b/object.c
@@ -532,7 +532,7 @@ void raw_object_store_clear(struct raw_object_store *o)
        close_object_store(o);
        o->packed_git = NULL;
 
-       hashmap_free(&o->pack_map);
+       hashmap_clear(&o->pack_map);
 }
 
 void parsed_object_pool_clear(struct parsed_object_pool *o)
index 423aa014a33eda2c6b695906eb0c349552d9e2b8..286a04a53c20119fe567eec4accbaba4d9f04a97 100644 (file)
--- a/oidmap.c
+++ b/oidmap.c
@@ -27,7 +27,7 @@ void oidmap_free(struct oidmap *map, int free_entries)
                return;
 
        /* TODO: make oidmap itself not depend on struct layouts */
-       hashmap_free_(&map->map, free_entries ? 0 : -1);
+       hashmap_clear_(&map->map, free_entries ? 0 : -1);
 }
 
 void *oidmap_get(const struct oidmap *map, const struct object_id *key)
index dad6d8ae7f23541a886e7cc6dc7a6b10dead10fe..4b089fe8ec051af05044225c283ee5ff3c1dac84 100644 (file)
@@ -39,7 +39,7 @@ int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
        } while (len);
 
        index_crc = p->index_data;
-       index_crc += 2 + 256 + p->num_objects * (the_hash_algo->rawsz/4) + nr;
+       index_crc += 2 + 256 + (size_t)p->num_objects * (the_hash_algo->rawsz/4) + nr;
 
        return data_crc != ntohl(*index_crc);
 }
@@ -164,7 +164,7 @@ static int verify_packfile(struct repository *r,
 
 int verify_pack_index(struct packed_git *p)
 {
-       off_t index_size;
+       size_t len;
        const unsigned char *index_base;
        git_hash_ctx ctx;
        unsigned char hash[GIT_MAX_RAWSZ];
@@ -172,14 +172,14 @@ int verify_pack_index(struct packed_git *p)
 
        if (open_pack_index(p))
                return error("packfile %s index not opened", p->pack_name);
-       index_size = p->index_size;
        index_base = p->index_data;
+       len = p->index_size - the_hash_algo->rawsz;
 
        /* Verify SHA1 sum of the index file */
        the_hash_algo->init_fn(&ctx);
-       the_hash_algo->update_fn(&ctx, index_base, (unsigned int)(index_size - the_hash_algo->rawsz));
+       the_hash_algo->update_fn(&ctx, index_base, len);
        the_hash_algo->final_fn(hash, &ctx);
-       if (!hasheq(hash, index_base + index_size - the_hash_algo->rawsz))
+       if (!hasheq(hash, index_base + len))
                err = error("Packfile index for %s hash mismatch",
                            p->pack_name);
        return err;
index d28a7e43d0bd80c3dc968439b3262da7c80117c1..ecdde39cf446f31e4c07f4d80cdb3767272c9cc8 100644 (file)
@@ -130,7 +130,7 @@ static void create_pack_revindex(struct packed_git *p)
 
        if (p->index_version > 1) {
                const uint32_t *off_32 =
-                       (uint32_t *)(index + 8 + p->num_objects * (hashsz + 4));
+                       (uint32_t *)(index + 8 + (size_t)p->num_objects * (hashsz + 4));
                const uint32_t *off_64 = off_32 + p->num_objects;
                for (i = 0; i < num_ent; i++) {
                        const uint32_t off = ntohl(*off_32++);
index 23e19cc1ecb2ed43f2559e45541f24c76ccde6f6..3513665e1e1be606dd116370fefbbb2a0d781245 100644 (file)
@@ -151,13 +151,10 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
                while (nr_large_offset) {
                        struct pack_idx_entry *obj = *list++;
                        uint64_t offset = obj->offset;
-                       uint32_t split[2];
 
                        if (!need_large_offset(offset, opts))
                                continue;
-                       split[0] = htonl(offset >> 32);
-                       split[1] = htonl(offset & 0xffffffff);
-                       hashwrite(f, split, 8);
+                       hashwrite_be64(f, offset);
                        nr_large_offset--;
                }
        }
index 0929ebe4fc747fc52c98d45542d9b257dc25a31d..9702b1218b86021ccd5d47b32dd59655899f63e0 100644 (file)
@@ -148,7 +148,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
                 *  - hash of the packfile
                 *  - file checksum
                 */
-               if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
+               if (idx_size != st_add(4 * 256 + hashsz + hashsz, st_mult(nr, hashsz + 4)))
                        return error("wrong index v1 file size in %s", path);
        } else if (version == 2) {
                /*
@@ -164,10 +164,10 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
                 * variable sized table containing 8-byte entries
                 * for offsets larger than 2^31.
                 */
-               unsigned long min_size = 8 + 4*256 + nr*(hashsz + 4 + 4) + hashsz + hashsz;
-               unsigned long max_size = min_size;
+               size_t min_size = st_add(8 + 4*256 + hashsz + hashsz, st_mult(nr, hashsz + 4 + 4));
+               size_t max_size = min_size;
                if (nr)
-                       max_size += (nr - 1)*8;
+                       max_size = st_add(max_size, st_mult(nr - 1, 8));
                if (idx_size < min_size || idx_size > max_size)
                        return error("wrong index v2 file size in %s", path);
                if (idx_size != min_size &&
@@ -1933,14 +1933,14 @@ off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
        const unsigned int hashsz = the_hash_algo->rawsz;
        index += 4 * 256;
        if (p->index_version == 1) {
-               return ntohl(*((uint32_t *)(index + (hashsz + 4) * n)));
+               return ntohl(*((uint32_t *)(index + (hashsz + 4) * (size_t)n)));
        } else {
                uint32_t off;
-               index += 8 + p->num_objects * (hashsz + 4);
+               index += 8 + (size_t)p->num_objects * (hashsz + 4);
                off = ntohl(*((uint32_t *)(index + 4 * n)));
                if (!(off & 0x80000000))
                        return off;
-               index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
+               index += (size_t)p->num_objects * 4 + (off & 0x7fffffff) * 8;
                check_pack_index_ptr(p, index);
                return get_be64(index);
        }
index 12aa6d494b4ed47d1a486307b6b9d9024ee78734..21973e49332c36532438bb067c8821a0e377baf9 100644 (file)
@@ -71,7 +71,7 @@ int init_patch_ids(struct repository *r, struct patch_ids *ids)
 
 int free_patch_ids(struct patch_ids *ids)
 {
-       hashmap_free_entries(&ids->patches, struct patch_id, ent);
+       hashmap_clear_and_free(&ids->patches, struct patch_id, ent);
        return 0;
 }
 
index 24dc435e482c0364ebc88e7ad55930f437d73ad2..b9950f10c8c486b16a2b916b0898d88c8d82fb59 100644 (file)
@@ -232,11 +232,9 @@ static int patch_util_cmp(const void *dummy, const struct patch_util *a,
 
 static void find_exact_matches(struct string_list *a, struct string_list *b)
 {
-       struct hashmap map;
+       struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL);
        int i;
 
-       hashmap_init(&map, (hashmap_cmp_fn)patch_util_cmp, NULL, 0);
-
        /* First, add the patches of a to a hash map */
        for (i = 0; i < a->nr; i++) {
                struct patch_util *util = a->items[i].util;
@@ -266,7 +264,7 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
                }
        }
 
-       hashmap_free(&map);
+       hashmap_clear(&map);
 }
 
 static void diffsize_consume(void *data, char *line, unsigned long len)
index 6476686fea1d03d9cc43d5c03f7c0b3fe3052030..aa260bfd0995564e085f69b5b1c7ecaf4b49f359 100644 (file)
@@ -2230,7 +2230,7 @@ void ref_array_clear(struct ref_array *array)
        used_atom_cnt = 0;
 
        if (ref_to_worktree_map.worktrees) {
-               hashmap_free_entries(&(ref_to_worktree_map.map),
+               hashmap_clear_and_free(&(ref_to_worktree_map.map),
                                        struct ref_to_worktree_entry, ent);
                free_worktrees(ref_to_worktree_map.worktrees);
                ref_to_worktree_map.worktrees = NULL;
index aa62212040814e29d17b8d1d7cafc15d1d50612d..c6e169e3eb3721b3690a6f822f5978c1718cb673 100644 (file)
@@ -124,11 +124,6 @@ static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,
        return strcmp(e1->path, e2->path);
 }
 
-static void paths_and_oids_init(struct hashmap *map)
-{
-       hashmap_init(map, path_and_oids_cmp, NULL, 0);
-}
-
 static void paths_and_oids_clear(struct hashmap *map)
 {
        struct hashmap_iter iter;
@@ -139,7 +134,7 @@ static void paths_and_oids_clear(struct hashmap *map)
                free(entry->path);
        }
 
-       hashmap_free_entries(map, struct path_and_oids_entry, ent);
+       hashmap_clear_and_free(map, struct path_and_oids_entry, ent);
 }
 
 static void paths_and_oids_insert(struct hashmap *map,
@@ -213,7 +208,7 @@ void mark_trees_uninteresting_sparse(struct repository *r,
                                     struct oidset *trees)
 {
        unsigned has_interesting = 0, has_uninteresting = 0;
-       struct hashmap map;
+       struct hashmap map = HASHMAP_INIT(path_and_oids_cmp, NULL);
        struct hashmap_iter map_iter;
        struct path_and_oids_entry *entry;
        struct object_id *oid;
@@ -237,8 +232,6 @@ void mark_trees_uninteresting_sparse(struct repository *r,
        if (!has_uninteresting || !has_interesting)
                return;
 
-       paths_and_oids_init(&map);
-
        oidset_iter_init(trees, &iter);
        while ((oid = oidset_iter_next(&iter))) {
                struct tree *tree = lookup_tree(r, oid);
index f6bf860d19e5a2997193c25873a5ba82e030f68b..086ff10280db586f2c714f989b10e4e8b082cf03 100644 (file)
@@ -238,6 +238,7 @@ struct rev_info {
        const char      *extra_headers;
        const char      *log_reencode;
        const char      *subject_prefix;
+       int             patch_name_max;
        int             no_inline;
        int             show_log_size;
        struct string_list *mailmap;
index 221e98721d23b398c115b3ae743cdc889d81515f..8909a467700c506f7068ed566aa7813c093157fd 100644 (file)
@@ -5085,7 +5085,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 
        oidmap_free(&commit2todo, 1);
        oidmap_free(&state.commit2label, 1);
-       hashmap_free_entries(&state.labels, struct labels_entry, entry);
+       hashmap_clear_and_free(&state.labels, struct labels_entry, entry);
        strbuf_release(&state.buf);
 
        return 0;
@@ -5601,7 +5601,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
        for (i = 0; i < todo_list->nr; i++)
                free(subjects[i]);
        free(subjects);
-       hashmap_free_entries(&subject2item, struct subject2item_entry, entry);
+       hashmap_clear_and_free(&subject2item, struct subject2item_entry, entry);
 
        clear_commit_todo_item(&commit_todo);
 
diff --git a/strmap.c b/strmap.c
new file mode 100644 (file)
index 0000000..4fb9f61
--- /dev/null
+++ b/strmap.c
@@ -0,0 +1,178 @@
+#include "git-compat-util.h"
+#include "strmap.h"
+#include "mem-pool.h"
+
+int cmp_strmap_entry(const void *hashmap_cmp_fn_data,
+                    const struct hashmap_entry *entry1,
+                    const struct hashmap_entry *entry2,
+                    const void *keydata)
+{
+       const struct strmap_entry *e1, *e2;
+
+       e1 = container_of(entry1, const struct strmap_entry, ent);
+       e2 = container_of(entry2, const struct strmap_entry, ent);
+       return strcmp(e1->key, e2->key);
+}
+
+static struct strmap_entry *find_strmap_entry(struct strmap *map,
+                                             const char *str)
+{
+       struct strmap_entry entry;
+       hashmap_entry_init(&entry.ent, strhash(str));
+       entry.key = str;
+       return hashmap_get_entry(&map->map, &entry, ent, NULL);
+}
+
+void strmap_init(struct strmap *map)
+{
+       strmap_init_with_options(map, NULL, 1);
+}
+
+void strmap_init_with_options(struct strmap *map,
+                             struct mem_pool *pool,
+                             int strdup_strings)
+{
+       hashmap_init(&map->map, cmp_strmap_entry, NULL, 0);
+       map->pool = pool;
+       map->strdup_strings = strdup_strings;
+}
+
+static void strmap_free_entries_(struct strmap *map, int free_values)
+{
+       struct hashmap_iter iter;
+       struct strmap_entry *e;
+
+       if (!map)
+               return;
+
+       if (!free_values && map->pool)
+               /* Memory other than util is owned by and freed with the pool */
+               return;
+
+       /*
+        * We need to iterate over the hashmap entries and free
+        * e->key and e->value ourselves; hashmap has no API to
+        * take care of that for us.  Since we're already iterating over
+        * the hashmap, though, might as well free e too and avoid the need
+        * to make some call into the hashmap API to do that.
+        */
+       hashmap_for_each_entry(&map->map, &iter, e, ent) {
+               if (free_values)
+                       free(e->value);
+               if (!map->pool)
+                       free(e);
+       }
+}
+
+void strmap_clear(struct strmap *map, int free_values)
+{
+       strmap_free_entries_(map, free_values);
+       hashmap_clear(&map->map);
+}
+
+void strmap_partial_clear(struct strmap *map, int free_values)
+{
+       strmap_free_entries_(map, free_values);
+       hashmap_partial_clear(&map->map);
+}
+
+static struct strmap_entry *create_entry(struct strmap *map,
+                                        const char *str,
+                                        void *data)
+{
+       struct strmap_entry *entry;
+
+       if (map->strdup_strings) {
+               if (!map->pool) {
+                       FLEXPTR_ALLOC_STR(entry, key, str);
+               } else {
+                       size_t len = st_add(strlen(str), 1); /* include NUL */
+                       entry = mem_pool_alloc(map->pool,
+                                              st_add(sizeof(*entry), len));
+                       memcpy(entry + 1, str, len);
+                       entry->key = (void *)(entry + 1);
+               }
+       } else if (!map->pool) {
+               entry = xmalloc(sizeof(*entry));
+       } else {
+               entry = mem_pool_alloc(map->pool, sizeof(*entry));
+       }
+       hashmap_entry_init(&entry->ent, strhash(str));
+       if (!map->strdup_strings)
+               entry->key = str;
+       entry->value = data;
+       return entry;
+}
+
+void *strmap_put(struct strmap *map, const char *str, void *data)
+{
+       struct strmap_entry *entry = find_strmap_entry(map, str);
+
+       if (entry) {
+               void *old = entry->value;
+               entry->value = data;
+               return old;
+       }
+
+       entry = create_entry(map, str, data);
+       hashmap_add(&map->map, &entry->ent);
+       return NULL;
+}
+
+struct strmap_entry *strmap_get_entry(struct strmap *map, const char *str)
+{
+       return find_strmap_entry(map, str);
+}
+
+void *strmap_get(struct strmap *map, const char *str)
+{
+       struct strmap_entry *entry = find_strmap_entry(map, str);
+       return entry ? entry->value : NULL;
+}
+
+int strmap_contains(struct strmap *map, const char *str)
+{
+       return find_strmap_entry(map, str) != NULL;
+}
+
+void strmap_remove(struct strmap *map, const char *str, int free_value)
+{
+       struct strmap_entry entry, *ret;
+       hashmap_entry_init(&entry.ent, strhash(str));
+       entry.key = str;
+       ret = hashmap_remove_entry(&map->map, &entry, ent, NULL);
+       if (!ret)
+               return;
+       if (free_value)
+               free(ret->value);
+       if (!map->pool)
+               free(ret);
+}
+
+void strintmap_incr(struct strintmap *map, const char *str, intptr_t amt)
+{
+       struct strmap_entry *entry = find_strmap_entry(&map->map, str);
+       if (entry) {
+               intptr_t *whence = (intptr_t*)&entry->value;
+               *whence += amt;
+       }
+       else
+               strintmap_set(map, str, map->default_value + amt);
+}
+
+int strset_add(struct strset *set, const char *str)
+{
+       /*
+        * Cannot use strmap_put() because it'll return NULL in both cases:
+        *   - cannot find str: NULL means "not found"
+        *   - does find str: NULL is the value associated with str
+        */
+       struct strmap_entry *entry = find_strmap_entry(&set->map, str);
+
+       if (entry)
+               return 0;
+
+       entry = create_entry(&set->map, str, NULL);
+       hashmap_add(&set->map.map, &entry->ent);
+       return 1;
+}
diff --git a/strmap.h b/strmap.h
new file mode 100644 (file)
index 0000000..c4c1044
--- /dev/null
+++ b/strmap.h
@@ -0,0 +1,268 @@
+#ifndef STRMAP_H
+#define STRMAP_H
+
+#include "hashmap.h"
+
+struct mem_pool;
+struct strmap {
+       struct hashmap map;
+       struct mem_pool *pool;
+       unsigned int strdup_strings:1;
+};
+
+struct strmap_entry {
+       struct hashmap_entry ent;
+       const char *key;
+       void *value;
+       /* strmap_entry may be allocated extra space to store the key at end */
+};
+
+int cmp_strmap_entry(const void *hashmap_cmp_fn_data,
+                    const struct hashmap_entry *entry1,
+                    const struct hashmap_entry *entry2,
+                    const void *keydata);
+
+#define STRMAP_INIT { \
+                       .map = HASHMAP_INIT(cmp_strmap_entry, NULL),  \
+                       .strdup_strings = 1,                          \
+                   }
+#define STRINTMAP_INIT { \
+                       .map = STRMAP_INIT,   \
+                       .default_value = 0,   \
+                      }
+#define STRSET_INIT { .map = STRMAP_INIT }
+
+/*
+ * Initialize the members of the strmap.  Any keys added to the strmap will
+ * be strdup'ed with their memory managed by the strmap.
+ */
+void strmap_init(struct strmap *map);
+
+/*
+ * Same as strmap_init, but for those who want to control the memory management
+ * carefully instead of using the default of strdup_strings=1 and pool=NULL.
+ */
+void strmap_init_with_options(struct strmap *map,
+                             struct mem_pool *pool,
+                             int strdup_strings);
+
+/*
+ * Remove all entries from the map, releasing any allocated resources.
+ */
+void strmap_clear(struct strmap *map, int free_values);
+
+/*
+ * Similar to strmap_clear() but leaves map->map->table allocated and
+ * pre-sized so that subsequent uses won't need as many rehashings.
+ */
+void strmap_partial_clear(struct strmap *map, int free_values);
+
+/*
+ * Insert "str" into the map, pointing to "data".
+ *
+ * If an entry for "str" already exists, its data pointer is overwritten, and
+ * the original data pointer returned. Otherwise, returns NULL.
+ */
+void *strmap_put(struct strmap *map, const char *str, void *data);
+
+/*
+ * Return the strmap_entry mapped by "str", or NULL if there is not such
+ * an item in map.
+ */
+struct strmap_entry *strmap_get_entry(struct strmap *map, const char *str);
+
+/*
+ * Return the data pointer mapped by "str", or NULL if the entry does not
+ * exist.
+ */
+void *strmap_get(struct strmap *map, const char *str);
+
+/*
+ * Return non-zero iff "str" is present in the map. This differs from
+ * strmap_get() in that it can distinguish entries with a NULL data pointer.
+ */
+int strmap_contains(struct strmap *map, const char *str);
+
+/*
+ * Remove the given entry from the strmap.  If the string isn't in the
+ * strmap, the map is not altered.
+ */
+void strmap_remove(struct strmap *map, const char *str, int free_value);
+
+/*
+ * Return how many entries the strmap has.
+ */
+static inline unsigned int strmap_get_size(struct strmap *map)
+{
+       return hashmap_get_size(&map->map);
+}
+
+/*
+ * Return whether the strmap is empty.
+ */
+static inline int strmap_empty(struct strmap *map)
+{
+       return strmap_get_size(map) == 0;
+}
+
+/*
+ * iterate through @map using @iter, @var is a pointer to a type strmap_entry
+ */
+#define strmap_for_each_entry(mystrmap, iter, var)     \
+       hashmap_for_each_entry(&(mystrmap)->map, iter, var, ent)
+
+
+/*
+ * strintmap:
+ *    A map of string -> int, typecasting the void* of strmap to an int.
+ *
+ * Primary differences:
+ *    1) Since the void* value is just an int in disguise, there is no value
+ *       to free.  (Thus one fewer argument to strintmap_clear)
+ *    2) strintmap_get() returns an int, or returns the default_value if the
+ *       key is not found in the strintmap.
+ *    3) No strmap_put() equivalent; strintmap_set() and strintmap_incr()
+ *       instead.
+ */
+
+struct strintmap {
+       struct strmap map;
+       int default_value;
+};
+
+#define strintmap_for_each_entry(mystrmap, iter, var)  \
+       strmap_for_each_entry(&(mystrmap)->map, iter, var)
+
+static inline void strintmap_init(struct strintmap *map, int default_value)
+{
+       strmap_init(&map->map);
+       map->default_value = default_value;
+}
+
+static inline void strintmap_init_with_options(struct strintmap *map,
+                                              int default_value,
+                                              struct mem_pool *pool,
+                                              int strdup_strings)
+{
+       strmap_init_with_options(&map->map, pool, strdup_strings);
+       map->default_value = default_value;
+}
+
+static inline void strintmap_clear(struct strintmap *map)
+{
+       strmap_clear(&map->map, 0);
+}
+
+static inline void strintmap_partial_clear(struct strintmap *map)
+{
+       strmap_partial_clear(&map->map, 0);
+}
+
+static inline int strintmap_contains(struct strintmap *map, const char *str)
+{
+       return strmap_contains(&map->map, str);
+}
+
+static inline void strintmap_remove(struct strintmap *map, const char *str)
+{
+       return strmap_remove(&map->map, str, 0);
+}
+
+static inline int strintmap_empty(struct strintmap *map)
+{
+       return strmap_empty(&map->map);
+}
+
+static inline unsigned int strintmap_get_size(struct strintmap *map)
+{
+       return strmap_get_size(&map->map);
+}
+
+/*
+ * Returns the value for str in the map.  If str isn't found in the map,
+ * the map's default_value is returned.
+ */
+static inline int strintmap_get(struct strintmap *map, const char *str)
+{
+       struct strmap_entry *result = strmap_get_entry(&map->map, str);
+       if (!result)
+               return map->default_value;
+       return (intptr_t)result->value;
+}
+
+static inline void strintmap_set(struct strintmap *map, const char *str,
+                                intptr_t v)
+{
+       strmap_put(&map->map, str, (void *)v);
+}
+
+/*
+ * Increment the value for str by amt.  If str isn't in the map, add it and
+ * set its value to default_value + amt.
+ */
+void strintmap_incr(struct strintmap *map, const char *str, intptr_t amt);
+
+/*
+ * strset:
+ *    A set of strings.
+ *
+ * Primary differences with strmap:
+ *    1) The value is always NULL, and ignored.  As there is no value to free,
+ *       there is one fewer argument to strset_clear
+ *    2) No strset_get() because there is no value.
+ *    3) No strset_put(); use strset_add() instead.
+ */
+
+struct strset {
+       struct strmap map;
+};
+
+#define strset_for_each_entry(mystrset, iter, var)     \
+       strmap_for_each_entry(&(mystrset)->map, iter, var)
+
+static inline void strset_init(struct strset *set)
+{
+       strmap_init(&set->map);
+}
+
+static inline void strset_init_with_options(struct strset *set,
+                                           struct mem_pool *pool,
+                                           int strdup_strings)
+{
+       strmap_init_with_options(&set->map, pool, strdup_strings);
+}
+
+static inline void strset_clear(struct strset *set)
+{
+       strmap_clear(&set->map, 0);
+}
+
+static inline void strset_partial_clear(struct strset *set)
+{
+       strmap_partial_clear(&set->map, 0);
+}
+
+static inline int strset_contains(struct strset *set, const char *str)
+{
+       return strmap_contains(&set->map, str);
+}
+
+static inline void strset_remove(struct strset *set, const char *str)
+{
+       return strmap_remove(&set->map, str, 0);
+}
+
+static inline int strset_empty(struct strset *set)
+{
+       return strmap_empty(&set->map);
+}
+
+static inline unsigned int strset_get_size(struct strset *set)
+{
+       return strmap_get_size(&set->map);
+}
+
+/* Returns 1 if str is added to the set; returns 0 if str was already in set */
+int strset_add(struct strset *set, const char *str);
+
+#endif /* STRMAP_H */
index c569e22aa3678bf694c080785f499d619b4d2e23..f50250556698231762092f4e4b6be68800c2089c 100644 (file)
@@ -103,8 +103,8 @@ static void submodule_cache_clear(struct submodule_cache *cache)
                                ent /* member name */)
                free_one_config(entry);
 
-       hashmap_free_entries(&cache->for_path, struct submodule_entry, ent);
-       hashmap_free_entries(&cache->for_name, struct submodule_entry, ent);
+       hashmap_clear_and_free(&cache->for_path, struct submodule_entry, ent);
+       hashmap_clear_and_free(&cache->for_name, struct submodule_entry, ent);
        cache->initialized = 0;
        cache->gitmodules_read = 0;
 }
index b3bb59f06644739c859adb22836e2761da2a91be..eef5204e641e198847e86e5e36c1115970831b8e 100644 (file)
@@ -499,12 +499,6 @@ void prepare_submodule_repo_env(struct strvec *out)
                     DEFAULT_GIT_DIR_ENVIRONMENT);
 }
 
-static void prepare_submodule_repo_env_in_gitdir(struct strvec *out)
-{
-       prepare_submodule_repo_env_no_git_dir(out);
-       strvec_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
-}
-
 /*
  * Initialize a repository struct for a submodule based on the provided 'path'.
  *
@@ -1455,8 +1449,8 @@ static int get_next_submodule(struct child_process *cp,
                if (task->repo) {
                        struct strbuf submodule_prefix = STRBUF_INIT;
                        child_process_init(cp);
-                       cp->dir = task->repo->gitdir;
-                       prepare_submodule_repo_env_in_gitdir(&cp->env_array);
+                       cp->dir = task->repo->worktree;
+                       prepare_submodule_repo_env(&cp->env_array);
                        cp->git_cmd = 1;
                        if (!spf->quiet)
                                strbuf_addf(err, _("Fetching submodule %s%s\n"),
@@ -1505,9 +1499,9 @@ static int get_next_submodule(struct child_process *cp,
                            spf->prefix, task->sub->path);
 
                child_process_init(cp);
-               prepare_submodule_repo_env_in_gitdir(&cp->env_array);
+               prepare_submodule_repo_env(&cp->env_array);
                cp->git_cmd = 1;
-               cp->dir = task->repo->gitdir;
+               cp->dir = task->repo->worktree;
 
                strvec_init(&cp->args);
                strvec_pushv(&cp->args, spf->args.v);
index f38706216f44c3c881f1d52b250fe74d5140bb37..36ff07bd4beaefabe5f42d119b0eac26cf3ae8d9 100644 (file)
@@ -110,7 +110,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
                                hashmap_add(&map, &entries[i]->ent);
                        }
 
-                       hashmap_free(&map);
+                       hashmap_clear(&map);
                }
        } else {
                /* test map lookups */
@@ -130,7 +130,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
                        }
                }
 
-               hashmap_free(&map);
+               hashmap_clear(&map);
        }
 }
 
@@ -151,12 +151,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
 int cmd__hashmap(int argc, const char **argv)
 {
        struct strbuf line = STRBUF_INIT;
-       struct hashmap map;
        int icase;
+       struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase);
 
        /* init hash map */
        icase = argc > 1 && !strcmp("ignorecase", argv[1]);
-       hashmap_init(&map, test_entry_cmp, &icase, 0);
 
        /* process commands from stdin */
        while (strbuf_getline(&line, stdin) != EOF) {
@@ -262,6 +261,6 @@ int cmd__hashmap(int argc, const char **argv)
        }
 
        strbuf_release(&line);
-       hashmap_free_entries(&map, struct test_entry, ent);
+       hashmap_clear_and_free(&map, struct test_entry, ent);
        return 0;
 }
index 42164d98983975bdfb9699268e6ff3d6d0c675ba..cc08506cf0bb73b1aefda41079f60cba1edcb9c1 100644 (file)
@@ -10,8 +10,11 @@ static const char *proc_receive_usage[] = {
        NULL
 };
 
-static int die_version;
-static int die_readline;
+static int die_read_version;
+static int die_write_version;
+static int die_read_commands;
+static int die_read_push_options;
+static int die_write_report;
 static int no_push_options;
 static int use_atomic;
 static int use_push_options;
@@ -33,14 +36,23 @@ struct command {
 static void proc_receive_verison(struct packet_reader *reader) {
        int server_version = 0;
 
+       if (die_read_version)
+               die("die with the --die-read-version option");
+
        for (;;) {
                int linelen;
 
                if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
+               /* Ignore version negotiation for version 0 */
+               if (version == 0)
+                       continue;
+
                if (reader->pktlen > 8 && starts_with(reader->line, "version=")) {
                        server_version = atoi(reader->line+8);
+                       if (server_version != 1)
+                               die("bad protocol version: %d", server_version);
                        linelen = strlen(reader->line);
                        if (linelen < reader->pktlen) {
                                const char *feature_list = reader->line + linelen + 1;
@@ -52,12 +64,13 @@ static void proc_receive_verison(struct packet_reader *reader) {
                }
        }
 
-       if (server_version != 1 || die_version)
-               die("bad protocol version: %d", server_version);
+       if (die_write_version)
+               die("die with the --die-write-version option");
 
-       packet_write_fmt(1, "version=%d%c%s\n",
-                        version, '\0',
-                        use_push_options && !no_push_options ? "push-options": "");
+       if (version != 0)
+               packet_write_fmt(1, "version=%d%c%s\n",
+                                version, '\0',
+                                use_push_options && !no_push_options ? "push-options": "");
        packet_flush(1);
 }
 
@@ -75,11 +88,13 @@ static void proc_receive_read_commands(struct packet_reader *reader,
                if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
+               if (die_read_commands)
+                       die("die with the --die-read-commands option");
+
                if (parse_oid_hex(reader->line, &old_oid, &p) ||
                    *p++ != ' ' ||
                    parse_oid_hex(p, &new_oid, &p) ||
-                   *p++ != ' ' ||
-                   die_readline)
+                   *p++ != ' ')
                        die("protocol error: expected 'old new ref', got '%s'",
                            reader->line);
                refname = p;
@@ -99,6 +114,9 @@ static void proc_receive_read_push_options(struct packet_reader *reader,
        if (no_push_options || !use_push_options)
               return;
 
+       if (die_read_push_options)
+               die("die with the --die-read-push-options option");
+
        while (1) {
                if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
@@ -117,10 +135,16 @@ int cmd__proc_receive(int argc, const char **argv)
        struct option options[] = {
                OPT_BOOL(0, "no-push-options", &no_push_options,
                         "disable push options"),
-               OPT_BOOL(0, "die-version", &die_version,
-                        "die during version negotiation"),
-               OPT_BOOL(0, "die-readline", &die_readline,
-                        "die when readline"),
+               OPT_BOOL(0, "die-read-version", &die_read_version,
+                        "die when reading version"),
+               OPT_BOOL(0, "die-write-version", &die_write_version,
+                        "die when writing version"),
+               OPT_BOOL(0, "die-read-commands", &die_read_commands,
+                        "die when reading commands"),
+               OPT_BOOL(0, "die-read-push-options", &die_read_push_options,
+                        "die when reading push-options"),
+               OPT_BOOL(0, "die-write-report", &die_write_report,
+                        "die when writing report"),
                OPT_STRING_LIST('r', "return", &returns, "old/new/ref/status/msg",
                                "return of results"),
                OPT__VERBOSE(&verbose, "be verbose"),
@@ -136,7 +160,7 @@ int cmd__proc_receive(int argc, const char **argv)
                usage_msg_opt("Too many arguments.", proc_receive_usage, options);
        packet_reader_init(&reader, 0, NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_DIE_ON_ERR_PACKET);
+                          PACKET_READ_GENTLE_ON_EOF);
 
        sigchain_push(SIGPIPE, SIG_IGN);
        proc_receive_verison(&reader);
@@ -166,6 +190,8 @@ int cmd__proc_receive(int argc, const char **argv)
                                fprintf(stderr, "proc-receive> %s\n", item->string);
        }
 
+       if (die_write_report)
+               die("die with the --die-write-report option");
        if (returns.nr)
                for_each_string_list_item(item, &returns)
                        packet_write_fmt(1, "%s\n", item->string);
index 492edffa9cfb626b4747ead04251fc33b0502ac9..dc9fe3cbf1b09ea6498cc73ca394adccef34d98a 100755 (executable)
@@ -144,4 +144,17 @@ test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
        test_must_fail git rev-parse --verify broken
 '
 
+test_expect_success 'options can appear after --verify' '
+       git rev-parse --verify HEAD >expect &&
+       git rev-parse --verify -q HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'verify respects --end-of-options' '
+       git update-ref refs/heads/-tricky HEAD &&
+       git rev-parse --verify HEAD >expect &&
+       git rev-parse --verify --end-of-options -tricky >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 3e657e693b272dec5711593ae85eec7c6725b9dc..e2ae15a2cf8542d066b3ea21d485d11689568e6e 100755 (executable)
@@ -254,4 +254,29 @@ test_expect_success 'escaped char does not trigger wildcard rule' '
        test_must_fail git rev-parse "foo\\*bar"
 '
 
+test_expect_success 'arg after dashdash not interpreted as option' '
+       cat >expect <<-\EOF &&
+       --
+       --local-env-vars
+       EOF
+       git rev-parse -- --local-env-vars >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'arg after end-of-options not interpreted as option' '
+       test_must_fail git rev-parse --end-of-options --not-real -- 2>err &&
+       test_i18ngrep bad.revision.*--not-real err
+'
+
+test_expect_success 'end-of-options still allows --' '
+       cat >expect <<-EOF &&
+       --end-of-options
+       $(git rev-parse --verify HEAD)
+       --
+       path
+       EOF
+       git rev-parse --end-of-options HEAD -- path >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 42588bf6e11fd5ff0aee94e0cffeab3a62f963c8..c5e5e0da3feef439415dbf9cfd235140eba7d0d9 100755 (executable)
@@ -313,6 +313,60 @@ test_expect_success 'multiple files' '
        ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
 '
 
+test_expect_success 'filename length limit' '
+       test_when_finished "rm -f 000*" &&
+       rm -rf 000[1-9]-*.patch &&
+       for len in 15 25 35
+       do
+               git format-patch --filename-max-length=$len -3 side &&
+               max=$(
+                       for patch in 000[1-9]-*.patch
+                       do
+                               echo "$patch" | wc -c
+                       done |
+                       sort -nr |
+                       head -n 1
+               ) &&
+               test $max -le $len || return 1
+       done
+'
+
+test_expect_success 'filename length limit from config' '
+       test_when_finished "rm -f 000*" &&
+       rm -rf 000[1-9]-*.patch &&
+       for len in 15 25 35
+       do
+               git -c format.filenameMaxLength=$len format-patch -3 side &&
+               max=$(
+                       for patch in 000[1-9]-*.patch
+                       do
+                               echo "$patch" | wc -c
+                       done |
+                       sort -nr |
+                       head -n 1
+               ) &&
+               test $max -le $len || return 1
+       done
+'
+
+test_expect_success 'filename limit applies only to basename' '
+       test_when_finished "rm -rf patches/" &&
+       rm -rf patches/ &&
+       for len in 15 25 35
+       do
+               git format-patch -o patches --filename-max-length=$len -3 side &&
+               max=$(
+                       for patch in patches/000[1-9]-*.patch
+                       do
+                               echo "${patch#patches/}" | wc -c
+                       done |
+                       sort -nr |
+                       head -n 1
+               ) &&
+               test $max -le $len || return 1
+       done
+'
+
 test_expect_success 'reroll count' '
        rm -fr patches &&
        git format-patch -o patches --cover-letter --reroll-count 4 master..side >list &&
index 8318781d2bbf6c345cda1fab26848771918f8a5a..1d40fcad3901c2c58b8e562fb596b34785671f5f 100755 (executable)
@@ -277,7 +277,7 @@ test_expect_success 'pack with missing parent' '
        git pack-objects --stdout --revs <revs >/dev/null
 '
 
-test_expect_success JGIT 'we can read jgit bitmaps' '
+test_expect_success JGIT,SHA1 'we can read jgit bitmaps' '
        git clone --bare . compat-jgit.git &&
        (
                cd compat-jgit.git &&
@@ -287,7 +287,7 @@ test_expect_success JGIT 'we can read jgit bitmaps' '
        )
 '
 
-test_expect_success JGIT 'jgit can read our bitmaps' '
+test_expect_success JGIT,SHA1 'jgit can read our bitmaps' '
        git clone --bare . compat-us.git &&
        (
                cd compat-us.git &&
index 521a3477108b9be56ed31f272b46ddcbca1db271..344d13f61a55cb57615bd56a1ada6e22b955829c 100644 (file)
@@ -42,7 +42,7 @@ create_commits_in () {
 make_user_friendly_and_stable_output () {
        sed \
                -e "s/  *\$//" \
-               -e "s/   */ /g" \
+               -e "s/  */ /g" \
                -e "s/'/\"/g" \
                -e "s/  /    /g" \
                -e "s/$A/<COMMIT-A>/g" \
@@ -54,3 +54,8 @@ make_user_friendly_and_stable_output () {
                -e "s#To $URL_PREFIX/upstream.git#To <URL/of/upstream.git>#" \
                -e "/^error: / d"
 }
+
+filter_out_user_friendly_and_stable_output () {
+       make_user_friendly_and_stable_output |
+               sed -n ${1+"$@"}
+}
index 2b04b4936743590796f687c893c70ce0253a6e08..47b058af7ede47e7c8c3bdc2a5238c24e9edbec4 100644 (file)
@@ -36,11 +36,10 @@ test_expect_success "git-push --atomic ($PROTOCOL)" '
                main \
                $B:refs/heads/next \
                >out 2>&1 &&
-       make_user_friendly_and_stable_output <out |
-               sed -n \
-                       -e "/^To / { s/   */ /g; p; }" \
-                       -e "/^ ! / { s/   */ /g; p; }" \
-                       >actual &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
+               <out >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [rejected] main -> main (non-fast-forward)
index 747307f8da746f14a7ea83863331948ad2967610..bbead12bbb48f5701d041e24954113795e2b90a2 100644 (file)
@@ -37,16 +37,15 @@ test_expect_success "git-push --atomic ($PROTOCOL/porcelain)" '
                main \
                $B:refs/heads/next \
                >out 2>&1 &&
-       make_user_friendly_and_stable_output <out |
-               sed -n \
-                       -e "s/^# GETTEXT POISON #//" \
-                       -e "/^To / { s/   */ /g; p; }" \
-                       -e "/^! / { s/   */ /g; p; }" \
-                       >actual &&
+       filter_out_user_friendly_and_stable_output \
+               -e "s/^# GETTEXT POISON #//" \
+               -e "/^To / { p; }" \
+               -e "/^! / { p; }" \
+               <out >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
-       ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward)
-       ! <COMMIT-B>:refs/heads/next [rejected] (atomic push failed)
+       !    refs/heads/main:refs/heads/main    [rejected] (non-fast-forward)
+       !    <COMMIT-B>:refs/heads/next    [rejected] (atomic push failed)
        EOF
        test_cmp expect actual &&
        git -C "$upstream" show-ref >out &&
index 854c3e884afe468c00a95feb7dfc9054d48511d9..b9be12be773bb4fa14d226202357afe08818b15b 100644 (file)
@@ -16,7 +16,8 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
 
        # Check status report for git-push
        sed -n \
-               -e "/^To / { p; n; p; }" \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
                <actual >actual-report &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
@@ -41,32 +42,98 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
        test_cmp expect actual
 '
 
-test_expect_success "setup proc-receive hook (hook --die-version, $PROTOCOL)" '
+test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL)" '
        write_script "$upstream/hooks/proc-receive" <<-EOF
        printf >&2 "# proc-receive hook\n"
-       test-tool proc-receive -v --die-version
+       test-tool proc-receive -v --die-read-version
        EOF
 '
 
 # Refs of upstream : main(A)
 # Refs of workbench: main(A)  tags/v123
 # git push         :                       refs/for/main/topic(A)
-test_expect_success "proc-receive: bad protocol (hook --die-version, $PROTOCOL)" '
+test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
                >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+        ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-read-version option" out &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+
+       git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-write-version
+       EOF
+'
 
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROTOCOL)" '
+       test_must_fail git -C workbench push origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+        ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-write-version option" out &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-read-commands
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROTOCOL)" '
+       test_must_fail git -C workbench push origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
+               <out >actual &&
        cat >expect <<-EOF &&
-       remote: # pre-receive hook
-       remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
-       remote: # proc-receive hook
-       remote: fatal: bad protocol version: 1
-       remote: error: proc-receive version "0" is not supported
        To <URL/of/upstream.git>
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-read-commands option" out &&
 
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
@@ -76,23 +143,65 @@ test_expect_success "proc-receive: bad protocol (hook --die-version, $PROTOCOL)"
        test_cmp expect actual
 '
 
-test_expect_success "setup proc-receive hook (hook --die-readline, $PROTOCOL)" '
+test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL)" '
        write_script "$upstream/hooks/proc-receive" <<-EOF
        printf >&2 "# proc-receive hook\n"
-       test-tool proc-receive -v --die-readline
+       test-tool proc-receive -v --die-read-push-options
        EOF
 '
 
 # Refs of upstream : main(A)
 # Refs of workbench: main(A)  tags/v123
 # git push         :                       refs/for/main/topic(A)
-test_expect_success "proc-receive: bad protocol (hook --die-readline, $PROTOCOL)" '
+test_expect_success "proc-receive: bad protocol (hook --die-read-push-options, $PROTOCOL)" '
+       git -C "$upstream" config receive.advertisePushOptions true &&
        test_must_fail git -C workbench push origin \
+               -o reviewers=user1,user2 \
                HEAD:refs/for/main/topic \
                >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+        ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-read-push-options option" out &&
+
+       git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-write-report
+       EOF
+'
 
-       grep "remote: fatal: protocol error: expected \"old new ref\", got \"<ZERO-OID> <COMMIT-A> refs/for/main/topic\"" actual &&
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTOCOL)" '
+       test_must_fail git -C workbench push origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; }" \
+               -e "/^ ! / { p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+        ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-write-report option" out &&
 
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
@@ -130,6 +239,7 @@ test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/main/topic (proc-receive failed to report status)
        EOF
        test_cmp expect actual &&
+
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
        cat >expect <<-EOF &&
@@ -173,6 +283,7 @@ test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/main/topic (proc-receive failed to report status)
        EOF
        test_cmp expect actual &&
+
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
        cat >expect <<-EOF &&
@@ -208,6 +319,7 @@ test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/main/topic (proc-receive failed to report status)
        EOF
        test_cmp expect actual &&
+
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
        cat >expect <<-EOF &&
index 88c56311da30730648f2143d700c448cef97abf1..fdb4569109ab0192abedd736a45e262f3b3335cb 100644 (file)
@@ -42,6 +42,175 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL/porc
        test_cmp expect actual
 '
 
+test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-read-version
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTOCOL/porcelain)" '
+       test_must_fail git -C workbench push --porcelain origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; n; p; n; p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+       !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
+       Done
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-read-version option" out &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-write-version
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROTOCOL/porcelain)" '
+       test_must_fail git -C workbench push --porcelain origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; n; p; n; p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+       !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
+       Done
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-write-version option" out &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-read-commands
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROTOCOL/porcelain)" '
+       test_must_fail git -C workbench push --porcelain origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; n; p; n; p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+       !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
+       Done
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-read-commands option" out &&
+
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-read-push-options
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-read-push-options, $PROTOCOL/porcelain)" '
+       git -C "$upstream" config receive.advertisePushOptions true &&
+       test_must_fail git -C workbench push --porcelain origin \
+               -o reviewers=user1,user2 \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; n; p; n; p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+       !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
+       Done
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-read-push-options option" out &&
+
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v --die-write-report
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push         :                       refs/for/main/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTOCOL/porcelain)" '
+       test_must_fail git -C workbench push --porcelain origin \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       filter_out_user_friendly_and_stable_output \
+               -e "/^To / { p; n; p; n; p; }" \
+               <out >actual &&
+       cat >expect <<-EOF &&
+       To <URL/of/upstream.git>
+       !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
+       Done
+       EOF
+       test_cmp expect actual &&
+       grep "remote: fatal: die with the --die-write-report option" out &&
+
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success "setup proc-receive hook (no report, $PROTOCOL/porcelain)" '
        write_script "$upstream/hooks/proc-receive" <<-EOF
        printf >&2 "# proc-receive hook\n"
@@ -71,6 +240,7 @@ test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL/porcelain)
        Done
        EOF
        test_cmp expect actual &&
+
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
        cat >expect <<-EOF &&
@@ -84,7 +254,6 @@ test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL/porcelain)
 # Refs of workbench: main(A)  tags/v123
 test_expect_success "cleanup ($PROTOCOL/porcelain)" '
        git -C "$upstream" update-ref -d refs/heads/next
-
 '
 
 test_expect_success "setup proc-receive hook (no ref, $PROTOCOL/porcelain)" '
@@ -115,6 +284,7 @@ test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
+
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
        cat >expect <<-EOF &&
@@ -151,6 +321,7 @@ test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL/porce
        Done
        EOF
        test_cmp expect actual &&
+
        git -C "$upstream" show-ref >out &&
        make_user_friendly_and_stable_output <out >actual &&
        cat >expect <<-EOF &&
index d414be87d0b7b8208fc8c1e004f547c664e0ff8b..e88edb16a445c88f008f2b82aab8c61e4ff47d55 100644 (file)
@@ -32,6 +32,66 @@ test_expect_success "enable push options ($PROTOCOL)" '
        git -C "$upstream" config receive.advertisePushOptions true
 '
 
+test_expect_success "setup version=0 for proc-receive hook ($PROTOCOL)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v \
+               --version 0 \
+               -r "ok refs/for/main/topic"
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push -o ...  :                       next(A)  refs/for/main/topic
+test_expect_success "proc-receive: ignore push-options for version 0 ($PROTOCOL)" '
+       git -C workbench push \
+               --atomic \
+               -o issue=123 \
+               -o reviewer=user1 \
+               origin \
+               HEAD:refs/heads/next \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       remote: # pre-receive hook
+       remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+       remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+       remote: # proc-receive hook
+       remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+       remote: proc-receive> ok refs/for/main/topic
+       remote: # post-receive hook
+       remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+       remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+       To <URL/of/upstream.git>
+        * [new branch] HEAD -> next
+        * [new reference] HEAD -> refs/for/main/topic
+       EOF
+       test_cmp expect actual &&
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       <COMMIT-A> refs/heads/next
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "restore proc-receive hook ($PROTOCOL)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v \
+               -r "ok refs/for/main/topic"
+       EOF
+'
+
+# Refs of upstream : main(A)             next(A)
+# Refs of workbench: main(A)  tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+       git -C "$upstream" update-ref -d refs/heads/next
+'
+
 # Refs of upstream : main(A)
 # Refs of workbench: main(A)  tags/v123
 # git push -o ...  :                       next(A)  refs/for/main/topic
index d5d0dcb172fd83565efdf308811f0927d0948fb6..3a6561b5eab465b3b9eab20e29a3c4fa992d6a7c 100644 (file)
@@ -33,6 +33,68 @@ test_expect_success "enable push options ($PROTOCOL/porcelain)" '
        git -C "$upstream" config receive.advertisePushOptions true
 '
 
+test_expect_success "setup version=0 for proc-receive hook ($PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v \
+               --version 0 \
+               -r "ok refs/for/main/topic"
+       EOF
+'
+
+# Refs of upstream : main(A)
+# Refs of workbench: main(A)  tags/v123
+# git push -o ...  :                       next(A)  refs/for/main/topic
+test_expect_success "proc-receive: ignore push-options for version 0 ($PROTOCOL/porcelain)" '
+       git -C workbench push \
+               --porcelain \
+               --atomic \
+               -o issue=123 \
+               -o reviewer=user1 \
+               origin \
+               HEAD:refs/heads/next \
+               HEAD:refs/for/main/topic \
+               >out 2>&1 &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       remote: # pre-receive hook
+       remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+       remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+       remote: # proc-receive hook
+       remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+       remote: proc-receive> ok refs/for/main/topic
+       remote: # post-receive hook
+       remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+       remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+       To <URL/of/upstream.git>
+       *    HEAD:refs/heads/next    [new branch]
+       *    HEAD:refs/for/main/topic    [new reference]
+       Done
+       EOF
+       test_cmp expect actual &&
+       git -C "$upstream" show-ref >out &&
+       make_user_friendly_and_stable_output <out >actual &&
+       cat >expect <<-EOF &&
+       <COMMIT-A> refs/heads/main
+       <COMMIT-A> refs/heads/next
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "restore proc-receive hook ($PROTOCOL/porcelain)" '
+       write_script "$upstream/hooks/proc-receive" <<-EOF
+       printf >&2 "# proc-receive hook\n"
+       test-tool proc-receive -v \
+               -r "ok refs/for/main/topic"
+       EOF
+'
+
+# Refs of upstream : main(A)             next(A)
+# Refs of workbench: main(A)  tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+       git -C "$upstream" update-ref -d refs/heads/next
+'
+
 # Refs of upstream : main(A)
 # Refs of workbench: main(A)  tags/v123
 # git push -o ...  :                       next(A)  refs/for/main/topic
index dd8e423d253b76bc3ee45dfece170423d3299a33..a7f6f9fdd6e595905e30f48cd4f18de3a19f643e 100755 (executable)
@@ -719,4 +719,67 @@ test_expect_success 'fetch new submodule commit intermittently referenced by sup
        )
 '
 
+add_commit_push () {
+       dir="$1"
+       msg="$2"
+       shift 2
+       git -C "$dir" add "$@" &&
+       git -C "$dir" commit -a -m "$msg" &&
+       git -C "$dir" push
+}
+
+compare_refs_in_dir () {
+       fail= &&
+       if test "x$1" = 'x!'
+       then
+               fail='!' &&
+               shift
+       fi &&
+       git -C "$1" rev-parse --verify "$2" >expect &&
+       git -C "$3" rev-parse --verify "$4" >actual &&
+       eval $fail test_cmp expect actual
+}
+
+
+test_expect_success 'setup nested submodule fetch test' '
+       # does not depend on any previous test setups
+
+       for repo in outer middle inner
+       do
+               (
+                       git init --bare $repo &&
+                       git clone $repo ${repo}_content &&
+                       echo "$repo" >"${repo}_content/file" &&
+                       add_commit_push ${repo}_content "initial" file
+               ) || return 1
+       done &&
+
+       git clone outer A &&
+       git -C A submodule add "$pwd/middle" &&
+       git -C A/middle/ submodule add "$pwd/inner" &&
+       add_commit_push A/middle/ "adding inner sub" .gitmodules inner &&
+       add_commit_push A/ "adding middle sub" .gitmodules middle &&
+
+       git clone outer B &&
+       git -C B/ submodule update --init middle &&
+
+       compare_refs_in_dir A HEAD B HEAD &&
+       compare_refs_in_dir A/middle HEAD B/middle HEAD &&
+       test -f B/file &&
+       test -f B/middle/file &&
+       ! test -f B/middle/inner/file &&
+
+       echo "change on inner repo of A" >"A/middle/inner/file" &&
+       add_commit_push A/middle/inner "change on inner" file &&
+       add_commit_push A/middle "change on inner" inner &&
+       add_commit_push A "change on inner" middle
+'
+
+test_expect_success 'fetching a superproject containing an uninitialized sub/sub project' '
+       # depends on previous test for setup
+
+       git -C B/ fetch &&
+       compare_refs_in_dir A origin/master B origin/master
+'
+
 test_done
index 524f30f7dc7c2388bad3885303541d4a72ed4470..a578b35761f2e97177dd8342ff5d949e719b64d3 100755 (executable)
@@ -728,6 +728,19 @@ test_expect_success 'add -N and difftool -d' '
        git difftool --dir-diff --extcmd ls
 '
 
+test_expect_success 'difftool --cached with unmerged files' '
+       test_when_finished git reset --hard &&
+
+       test_commit conflicting &&
+       test_commit conflict-a conflict.t a &&
+       git reset --hard conflicting &&
+       test_commit conflict-b conflict.t b &&
+       test_must_fail git merge conflict-a &&
+
+       git difftool --cached --no-prompt >output &&
+       test_must_be_empty output
+'
+
 test_expect_success 'outside worktree' '
        echo 1 >1 &&
        echo 2 >2 &&
index 2be9190425bb1dc6f9ab0eb1b8e030c99e0d2920..5c01c75d408955ed75384454394ad843f94d4263 100755 (executable)
@@ -2195,6 +2195,25 @@ test_expect_success 'complete files' '
        test_completion "git add mom" "momified"
 '
 
+test_expect_success "simple alias" '
+       test_config alias.co checkout &&
+       test_completion "git co m" <<-\EOF
+       master Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
+test_expect_success "recursive alias" '
+       test_config alias.co checkout &&
+       test_config alias.cod "co --detached" &&
+       test_completion "git cod m" <<-\EOF
+       master Z
+       mybranch Z
+       mytag Z
+       EOF
+'
+
 test_expect_success "completion uses <cmd> completion for alias: !sh -c 'git <cmd> ...'" '
        test_config alias.co "!sh -c '"'"'git checkout ...'"'"'" &&
        test_completion "git co m" <<-\EOF